test(runner): байтовые проверки канона вместо масок (#57)

Раннер сам прятал дефекты, которые чинит #57. normalizeXmlContent при
runtime=python снимал ровно четыре измерения: пробел перед `/>`, whitespace между
тегами, пустую пару <Tag></Tag> и хвостовой пробельный мусор. Паритет PS<->PY по
этим измерениям проверялся ЧЕРЕЗ маску — расхождение физически не могло упасть.

Снято три из четырёх (пробел, пустая пара, хвост). Схлопывание whitespace между
тегами оставлено: порты кое-где расставляют отступы иначе, это форматирование, а
не байтовый канон.

Ужесточён checkPreserves: eol теперь считает ОДИНОЧНЫЕ LF, а не «есть ли хоть один
CR». Прежняя проверка пропускала смешанный выход — cfe-init давал 10 CR на 70
строк и проходил её, то есть головной дефект тикета был ей невидим. Добавлены
ключи selfClose:"tight" и noEmptyPairs; preserves проставлен в 13 кейсах
навыков-эмиттеров (по одному на навык, на его СОБСТВЕННЫЙ артефакт).

Снятие масок сразу вскрыло четыре реальных расхождения портов:

- form-edit собирает выход из OuterXml и писал `<a />`; в списке 17 навыков его не
  было, потому что искал по вызовам Save — здесь другой путь. Тот же случай, что
  с Form.xml в cfe-borrow;
- meta-edit py дописывал хвостовой перевод в создаваемый Ext/Predefined.xml и
  читал существующий без newline='' (терял CRLF);
- skd-info py писал отчёт -OutFile без хвостового перевода, PS через WriteAllLines
  — с ним. Это текстовый отчёт, канон Конфигуратора к нему не относится, поэтому
  выровнял py по существующему эталону;
- фикстуры кейсов содержали <Vendor></Vendor> — снимок нашего же старого вывода.
  Конфигуратор пустых пар не пишет, .NET их сохраняет, lxml схлопывает. Поправлены
  10 фикстур: пары → самозакрывающиеся, EOL и BOM не тронуты.

Результат: PS 641/641, python 638/641 (+3 runtimeOnly-скипа) — со снятыми масками.
Дрейф снэпшотов: 10 файлов, только пробельные теги и пустые пары.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-05 16:23:13 +03:00
co-authored by Claude Opus 5
parent 11e4e4301f
commit d7fd6d79d3
41 changed files with 361 additions and 106 deletions
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<UpdateCatalogAddress/>
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
@@ -48,7 +48,7 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Vendor/>
<Version>1.0.0.2</Version>
<UpdateCatalogAddress/>
<IncludeHelpInContents>false</IncludeHelpInContents>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
+12 -2
View File
@@ -8,6 +8,16 @@
"Configuration.xml",
"Languages/Русский.xml",
"Ext/ClientApplicationInterface.xml"
]
],
"preserves": {
"file": "Configuration.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
}
@@ -32,7 +32,7 @@
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name></Name>
<Name/>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<DataLockControlMode>Managed</DataLockControlMode>
+17 -2
View File
@@ -1,8 +1,23 @@
{
"name": "Пустое расширение",
"params": { "name": "МоёРасширение", "outputDir": "ext" },
"params": {
"name": "МоёРасширение",
"outputDir": "ext"
},
"validatePath": "ext",
"expect": {
"files": ["ext/Configuration.xml"]
"files": [
"ext/Configuration.xml"
],
"preserves": {
"file": "Configuration.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -8,7 +8,7 @@
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name></Name>
<Name/>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
</Properties>
<ChildObjects/>
+16 -2
View File
@@ -1,8 +1,22 @@
{
"name": "Пустая внешняя обработка",
"params": { "name": "ТестоваяОбработка" },
"params": {
"name": "ТестоваяОбработка"
},
"validatePath": "ТестоваяОбработка",
"expect": {
"files": ["ТестоваяОбработка.xml"]
"files": [
"ТестоваяОбработка.xml"
],
"preserves": {
"file": "ТестоваяОбработка.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
+16 -2
View File
@@ -1,8 +1,22 @@
{
"name": "Пустой внешний отчёт",
"params": { "name": "ТестовыйОтчёт" },
"params": {
"name": "ТестовыйОтчёт"
},
"validatePath": "ТестовыйОтчёт",
"expect": {
"files": ["ТестовыйОтчёт.xml"]
"files": [
"ТестовыйОтчёт.xml"
],
"preserves": {
"file": "ТестовыйОтчёт.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
+25 -4
View File
@@ -3,10 +3,31 @@
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "Catalog", "name": "Товары" },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
"input": {
"type": "Catalog",
"name": "Товары"
},
"args": {
"-JsonPath": "{inputFile}",
"-OutputDir": "{workDir}"
}
}
],
"params": { "objectPath": "Catalogs/Товары.xml", "formName": "ФормаЭлемента" },
"validatePath": "Catalogs/Товары/Forms/ФормаЭлемента"
"params": {
"objectPath": "Catalogs/Товары.xml",
"formName": "ФормаЭлемента"
},
"validatePath": "Catalogs/Товары/Forms/ФормаЭлемента",
"expect": {
"preserves": {
"file": "Catalogs/Товары/Forms/ФормаЭлемента/Ext/Form.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
+27 -4
View File
@@ -3,17 +3,40 @@
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "DataProcessor", "name": "Минимальная" },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
"input": {
"type": "DataProcessor",
"name": "Минимальная"
},
"args": {
"-JsonPath": "{inputFile}",
"-OutputDir": "{workDir}"
}
},
{
"script": "form-add/scripts/form-add",
"args": { "-ObjectPath": "{workDir}/DataProcessors/Минимальная.xml", "-FormName": "Форма" }
"args": {
"-ObjectPath": "{workDir}/DataProcessors/Минимальная.xml",
"-FormName": "Форма"
}
}
],
"params": { "outputPath": "DataProcessors/Минимальная/Forms/Форма/Ext/Form.xml" },
"params": {
"outputPath": "DataProcessors/Минимальная/Forms/Форма/Ext/Form.xml"
},
"validatePath": "DataProcessors/Минимальная/Forms/Форма/Ext/Form.xml",
"input": {
"title": "Минимальная форма"
},
"expect": {
"preserves": {
"file": "DataProcessors/Минимальная/Forms/Форма/Ext/Form.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -7,7 +7,7 @@
</v8:item>
</Title>
<AutoTitle>false</AutoTitle>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1" />
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<Attributes>
<Attribute name="Объект" id="1">
<Type>
@@ -7,12 +7,12 @@
</v8:item>
</Title>
<AutoTitle>false</AutoTitle>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1" />
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<ChildItems>
<Button name="Выполнить" id="1">
<CommandName>Form.Command.Выполнить</CommandName>
<DefaultButton>true</DefaultButton>
<ExtendedTooltip name="ВыполнитьРасширеннаяПодсказка" id="2" />
<ExtendedTooltip name="ВыполнитьРасширеннаяПодсказка" id="2"/>
</Button>
</ChildItems>
<Attributes>
@@ -7,7 +7,7 @@
</v8:item>
</Title>
<AutoTitle>false</AutoTitle>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1" />
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<ChildItems>
<InputField name="Поле1" id="1">
<DataPath>Поле1</DataPath>
@@ -17,8 +17,8 @@
<v8:content>Поле 1</v8:content>
</v8:item>
</Title>
<ContextMenu name="Поле1КонтекстноеМеню" id="2" />
<ExtendedTooltip name="Поле1РасширеннаяПодсказка" id="3" />
<ContextMenu name="Поле1КонтекстноеМеню" id="2"/>
<ExtendedTooltip name="Поле1РасширеннаяПодсказка" id="3"/>
</InputField>
<InputField name="Поле2" id="4">
<DataPath>Поле2</DataPath>
@@ -28,8 +28,8 @@
<v8:content>Поле 2</v8:content>
</v8:item>
</Title>
<ContextMenu name="Поле2КонтекстноеМеню" id="5" />
<ExtendedTooltip name="Поле2РасширеннаяПодсказка" id="6" />
<ContextMenu name="Поле2КонтекстноеМеню" id="5"/>
<ExtendedTooltip name="Поле2РасширеннаяПодсказка" id="6"/>
</InputField>
</ChildItems>
<Attributes>
@@ -7,7 +7,7 @@
</v8:item>
</Title>
<AutoTitle>false</AutoTitle>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1" />
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<ChildItems>
<InputField name="Поле1" id="1">
<DataPath>Поле1</DataPath>
@@ -17,8 +17,8 @@
<v8:content>Существующее поле</v8:content>
</v8:item>
</Title>
<ContextMenu name="Поле1КонтекстноеМеню" id="2" />
<ExtendedTooltip name="Поле1РасширеннаяПодсказка" id="3" />
<ContextMenu name="Поле1КонтекстноеМеню" id="2"/>
<ExtendedTooltip name="Поле1РасширеннаяПодсказка" id="3"/>
</InputField>
<UsualGroup name="ГруппаНовая" id="4">
<Title>
@@ -28,7 +28,7 @@
</v8:item>
</Title>
<Group>Horizontal</Group>
<ExtendedTooltip name="ГруппаНоваяРасширеннаяПодсказка" id="5" />
<ExtendedTooltip name="ГруппаНоваяРасширеннаяПодсказка" id="5"/>
<ChildItems>
<InputField name="Поле2" id="6">
<DataPath>Поле2</DataPath>
@@ -38,8 +38,8 @@
<v8:content>Поле 2</v8:content>
</v8:item>
</Title>
<ContextMenu name="Поле2КонтекстноеМеню" id="7" />
<ExtendedTooltip name="Поле2РасширеннаяПодсказка" id="8" />
<ContextMenu name="Поле2КонтекстноеМеню" id="7"/>
<ExtendedTooltip name="Поле2РасширеннаяПодсказка" id="8"/>
</InputField>
<InputField name="Поле3" id="9">
<DataPath>Поле3</DataPath>
@@ -49,8 +49,8 @@
<v8:content>Поле 3</v8:content>
</v8:item>
</Title>
<ContextMenu name="Поле3КонтекстноеМеню" id="10" />
<ExtendedTooltip name="Поле3РасширеннаяПодсказка" id="11" />
<ContextMenu name="Поле3КонтекстноеМеню" id="10"/>
<ExtendedTooltip name="Поле3РасширеннаяПодсказка" id="11"/>
</InputField>
</ChildItems>
</UsualGroup>
+19 -2
View File
@@ -3,8 +3,25 @@
"preRun": [
{
"script": "epf-init/scripts/init",
"args": { "-Name": "МояОбработка", "-SrcDir": "{workDir}" }
"args": {
"-Name": "МояОбработка",
"-SrcDir": "{workDir}"
}
}
],
"params": { "objectName": "МояОбработка" }
"params": {
"objectName": "МояОбработка"
},
"expect": {
"preserves": {
"file": "МояОбработка.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -1,8 +1,24 @@
{
"name": "Простой справочник без реквизитов",
"input": { "type": "Catalog", "name": "Валюты" },
"input": {
"type": "Catalog",
"name": "Валюты"
},
"validatePath": "Catalogs/Валюты",
"expect": {
"files": ["Catalogs/Валюты.xml", "Catalogs/Валюты/Ext/ObjectModule.bsl"]
"files": [
"Catalogs/Валюты.xml",
"Catalogs/Валюты/Ext/ObjectModule.bsl"
],
"preserves": {
"file": "Catalogs/Валюты.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
@@ -97,7 +97,7 @@
</v8:item>
</Synonym>
<Comment/>
<Group></Group>
<Group/>
<CommandParameterType>
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.ТестКоманд</v8:Type>
</CommandParameterType>
+24 -3
View File
@@ -6,14 +6,35 @@
{
"name": "Ячейка",
"rows": [
{ "cells": [{ "col": 1, "text": "Значение" }] }
{
"cells": [
{
"col": 1,
"text": "Значение"
}
]
}
]
}
]
},
"params": { "outputPath": "Template.xml" },
"params": {
"outputPath": "Template.xml"
},
"validatePath": "Template.xml",
"expect": {
"files": ["Template.xml"]
"files": [
"Template.xml"
],
"preserves": {
"file": "Template.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -24,6 +24,16 @@
"files": [
"Roles/Кладовщик.xml",
"Roles/Кладовщик/Ext/Rights.xml"
]
],
"preserves": {
"file": "Roles/Кладовщик/Ext/Rights.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
+24 -6
View File
@@ -1,14 +1,32 @@
{
"name": "Минимальная СКД — один набор, один запрос",
"params": { "outputPath": "Template.xml" },
"params": {
"outputPath": "Template.xml"
},
"input": {
"dataSets": [{
"query": "ВЫБРАТЬ Номенклатура.Наименование КАК Наименование ИЗ Справочник.Номенклатура КАК Номенклатура",
"fields": ["Наименование"]
}]
"dataSets": [
{
"query": "ВЫБРАТЬ Номенклатура.Наименование КАК Наименование ИЗ Справочник.Номенклатура КАК Номенклатура",
"fields": [
"Наименование"
]
}
]
},
"validatePath": "Template.xml",
"expect": {
"files": ["Template.xml"]
"files": [
"Template.xml"
],
"preserves": {
"file": "Template.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -16,8 +16,8 @@
<dataSource>ИсточникДанных1</dataSource>
<query>SELECT 1</query>
<field xsi:type="DataSetFieldField">
<dataPath></dataPath>
<field></field>
<dataPath/>
<field/>
</field>
</dataSet>
<settingsVariant>
@@ -3,17 +3,37 @@
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "Catalog", "name": "Товары" },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
"input": {
"type": "Catalog",
"name": "Товары"
},
"args": {
"-JsonPath": "{inputFile}",
"-OutputDir": "{workDir}"
}
}
],
"input": {
"name": "Склад",
"synonym": "Склад",
"content": ["Catalogs.Товары"]
"content": [
"Catalogs.Товары"
]
},
"validatePath": "Subsystems/Склад",
"expect": {
"files": ["Subsystems/Склад.xml"]
"files": [
"Subsystems/Склад.xml"
],
"preserves": {
"file": "Subsystems/Склад.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
@@ -48,8 +48,8 @@
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<Vendor/>
<Version/>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
+20 -3
View File
@@ -1,9 +1,26 @@
{
"name": "минимальный пакет: один objectType, два свойства",
"caseFiles": ["minimal.xsd"],
"params": { "xsdFile": "minimal.xsd" },
"caseFiles": [
"minimal.xsd"
],
"params": {
"xsdFile": "minimal.xsd"
},
"validatePath": "XDTOPackages/minimal",
"expect": {
"files": ["XDTOPackages/minimal.xml", "XDTOPackages/minimal/Ext/Package.bin"]
"files": [
"XDTOPackages/minimal.xml",
"XDTOPackages/minimal/Ext/Package.bin"
],
"preserves": {
"file": "XDTOPackages/minimal.xml",
"bom": true,
"eol": "crlf",
"encoding": "UTF-8",
"finalNewline": false,
"noCR13": true,
"selfClose": "tight",
"noEmptyPairs": true
}
}
}