feat(skd-validate): проверка схемы компоновки по XSD платформы (#101)

Если в проекте есть схемы /v8-xsd-fetch, skd-validate дополнительно
проверяет порядок элементов, типы и значения по XSD версии формата
выгрузки (штамп Configuration.xml или корня внешнего отчёта). Схемы
точной версии — ошибки, только более новой — предупреждения, иначе
проверка пропускается. Места, которые платформа пишет иначе, чем
описывает XSD, отсекаются по месту нарушения: на 2606 схемах компоновки
выгрузок БП, УНФ и ERP (форматы 2.17–2.21) — ни одного срабатывания в
обоих портах.

Runner: поле кейса requires (путь от корня репозитория, нет — skipped)
и плейсхолдер {repoRoot} — кейсы на настоящих схемах, которые в
репозиторий не входят.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018dc2yLQfw6wppbopM1Y4Ps
This commit is contained in:
Nick Shirokov
2026-09-29 15:24:26 +03:00
co-authored by Claude Opus 5.5
parent 9293305a12
commit 5465a7803c
25 changed files with 1594 additions and 12 deletions
+2
View File
@@ -12,6 +12,8 @@ allowed-tools:
Проверяет структурную корректность Template.xml схемы компоновки данных. Выявляет ошибки формата, битые ссылки, дубликаты имён. Проверяет структурную корректность Template.xml схемы компоновки данных. Выявляет ошибки формата, битые ссылки, дубликаты имён.
Если в проекте есть XSD-схемы платформы (`/v8-xsd-fetch`), дополнительно проверяет порядок элементов, типы и значения по схеме версии формата выгрузки.
## Параметры ## Параметры
| Параметр | Обяз. | Умолч. | Описание | | Параметр | Обяз. | Умолч. | Описание |
@@ -1,4 +1,4 @@
# skd-validate v1.4 — Validate 1C DCS structure # skd-validate v1.5 — Validate 1C DCS structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
[CmdletBinding(PositionalBinding=$false)] [CmdletBinding(PositionalBinding=$false)]
param( param(
@@ -926,6 +926,222 @@ if ($vChecked -gt 0 -and $vOk) {
if ($script:stopped) { & $finalize; exit 1 } if ($script:stopped) { & $finalize; exit 1 }
# --- 18. XSD (schemas of the platform, /v8-xsd-fetch) ---
# Порядок элементов, типы и значения — по XSD платформы, если они есть в проекте. Нет схем —
# нет проверки. Схемы XDTO совпадают с выгрузкой конфигуратора только в СКД, и то кроме
# нескольких мест, которые платформа пишет иначе, — они отсекаются ниже (Test-XsdPlatformNoise).
# Копия общего эталона (семья find_v8project, авторитет — cf-edit).
function Find-V8Project([string]$startDir) {
$d = $startDir
for ($i = 0; $i -lt 20 -and $d; $i++) {
$pj = Join-Path $d ".v8-project.json"
if (Test-Path $pj) { return $pj }
$parent = [System.IO.Path]::GetDirectoryName($d)
if ($parent -eq $d) { break }
$d = $parent
}
return $null
}
# Штамп версии формата — атрибут version КОРНЕВОГО элемента файла. Именно корневого: в Form.xml
# расширения ниже стоит <BaseForm version=…>. Читается только заголовок, без разбора файла.
function Get-RootVersion([string]$xmlPath) {
if (-not (Test-Path -LiteralPath $xmlPath -PathType Leaf)) { return $null }
$buf = New-Object byte[] 4096
$fs = [System.IO.File]::OpenRead($xmlPath)
try { $len = $fs.Read($buf, 0, $buf.Length) } finally { $fs.Dispose() }
$head = [System.Text.Encoding]::UTF8.GetString($buf, 0, $len)
$m = [regex]::Match($head, '<[A-Za-z_][\w.:-]*(\s[^>]*)?/?>')
if (-not $m.Success) { return $null }
$v = [regex]::Match($m.Groups[1].Value, '(?:^|\s)version="([^"]*)"')
if ($v.Success) { return $v.Groups[1].Value }
return $null
}
# Корень автономной внешней обработки/отчёта. Копия общего эталона (семья is_external_root,
# авторитет — cf-edit).
function Test-ExternalObjectRoot([string]$xmlPath) {
if (-not (Test-Path $xmlPath)) { return $false }
try {
[xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8
$el = $mx.DocumentElement.FirstChild
while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling }
if ($el) { return @('ExternalDataProcessor','ExternalReport') -contains $el.LocalName }
} catch {}
return $false
}
# Якорь выгрузки, в чьём дереве лежит файл: корень автономной EPF/ERF либо Configuration.xml,
# ближайший вверх. Корень обработки проверяется первым — иначе обработка, лежащая в дереве
# конфигурации, сверялась бы с конфигурацией. Нет якоря — сверять не с чем.
function Find-DumpAnchor([string]$startDir) {
$d = $startDir
for ($i = 0; $i -lt 15 -and $d; $i++) {
if (Test-ExternalObjectRoot "$d.xml") { return "$d.xml" }
$cfg = Join-Path $d "Configuration.xml"
if (Test-Path $cfg) { return $cfg }
$parent = [System.IO.Path]::GetDirectoryName($d)
if (-not $parent -or $parent -eq $d) { break }
$d = $parent
}
return $null
}
function Get-FormatRank([string]$ver) {
if ($ver -match '^(\d+)\.(\d+)$') { return [int]$Matches[1] * 100 + [int]$Matches[2] }
return 0
}
$xsdDcsNs = "http://v8.1c.ru/8.1/data-composition-system/schema"
# targetNamespace схемы — из заголовка файла, без разбора
function Get-XsdTargetNs([string]$path) {
$buf = New-Object byte[] 4096
$fs = [System.IO.File]::OpenRead($path)
try { $len = $fs.Read($buf, 0, $buf.Length) } finally { $fs.Dispose() }
$m = [regex]::Match([System.Text.Encoding]::UTF8.GetString($buf, 0, $len), 'targetNamespace="([^"]*)"')
if ($m.Success) { return $m.Groups[1].Value }
return $null
}
# Каталог версии годится, если в нём есть схема СКД (в 2.10–2.11 её нет)
function Test-XsdVersionDir([string]$dir) {
foreach ($f in Get-ChildItem -LiteralPath $dir -Filter *.xsd -File -ErrorAction SilentlyContinue) {
if ((Get-XsdTargetNs $f.FullName) -ceq $xsdDcsNs) { return $true }
}
return $false
}
# Каталог схем: .v8-project.json — сначала от шаблона (проект, которому он принадлежит), потом
# от текущего каталога; xsdPath из него, иначе .v8-xsd рядом с ним. Нет проекта — нет схем.
function Resolve-XsdRoot([string]$templateDir) {
$pj = Find-V8Project $templateDir
if (-not $pj) { $pj = Find-V8Project (Get-Location).Path }
if (-not $pj) { return $null }
$pjDir = [System.IO.Path]::GetDirectoryName($pj)
$rel = ".v8-xsd"
try {
$cfg = Get-Content -LiteralPath $pj -Raw -Encoding UTF8 | ConvertFrom-Json
if ($cfg.xsdPath) { $rel = "$($cfg.xsdPath)" }
} catch {}
$dir = if ([System.IO.Path]::IsPathRooted($rel)) { $rel } else { Join-Path $pjDir $rel }
if (Test-Path -LiteralPath $dir -PathType Container) { return $dir }
return $null
}
# Места, которые платформа пишет не так, как описывает XSD, — сверено на всех схемах компоновки
# выгрузок БП и УНФ. Правило — по месту (элемент, родитель, xsi:type), не по тексту: текст
# зависит от движка и языка.
function Test-XsdPlatformNoise($info) {
$e = $info.elem; $p = $info.parent
if (($p -ceq 'groupTemplate' -or $p -ceq 'groupHeaderTemplate') -and ($e -ceq 'templateType' -or $e -ceq 'groupName')) { return $true }
if ($p -ceq 'right' -and $e -ceq 'lastId') { return $true }
if ($p -ceq 'nestedSchema' -and $e -ceq 'schema') { return $true }
if ($info.xsiNs -ceq 'http://v8.1c.ru/8.2/data/chart') { return $true }
# Пустое выражение параметра макета платформа не пишет, схема требует
if ($e -ceq 'parameter' -and $info.xsiLocal -ceq 'ExpressionAreaTemplateParameter') { return $true }
return $false
}
function Invoke-XsdCheck {
$xsdRoot = Resolve-XsdRoot ([System.IO.Path]::GetDirectoryName($resolvedPath))
if (-not $xsdRoot) { Report-OK "XSD: no schemas in project — not checked"; return }
$anchor = Find-DumpAnchor ([System.IO.Path]::GetDirectoryName($resolvedPath))
$ver = if ($anchor) { Get-RootVersion $anchor } else { $null }
if (-not $ver -or (Get-FormatRank $ver) -eq 0) { Report-OK "XSD: format version unknown (template outside a dump) — not checked"; return }
# Точная версия — ошибки; ближайшая более новая — предупреждения; только старше — пропуск
$cands = @(Get-ChildItem -LiteralPath $xsdRoot -Directory |
Where-Object { $_.Name -match '^\d+\.\d+$' -and (Get-FormatRank $_.Name) -ge (Get-FormatRank $ver) } |
Sort-Object { Get-FormatRank $_.Name } |
Where-Object { Test-XsdVersionDir $_.FullName })
if ($cands.Count -eq 0) { Report-Warn "XSD: no schemas for format $ver in '$xsdRoot' — not checked"; return }
$useDir = $cands[0].FullName
$useVer = $cands[0].Name
$exact = ($useVer -eq $ver)
# Набор схем. У import нет schemaLocation — связь по namespace, поэтому в набор кладём все.
# Корень DataCompositionSchema в схеме объявлен как dataCompositionSchema (строчная) —
# дописываем элемент в саму схему СКД: вторую схему того же namespace без SourceUri
# XmlSchemaSet молча отбрасывает.
$schemas = @()
foreach ($f in Get-ChildItem -LiteralPath $useDir -Filter *.xsd -File) {
$fs = [System.IO.File]::OpenRead($f.FullName)
try { $schemas += [System.Xml.Schema.XmlSchema]::Read($fs, $null) } finally { $fs.Dispose() }
}
$dcs = $schemas | Where-Object { $_.TargetNamespace -ceq $xsdDcsNs } | Select-Object -First 1
$hasRoot = $false
foreach ($it in $dcs.Items) {
if ($it -is [System.Xml.Schema.XmlSchemaElement] -and $it.Name -ceq 'DataCompositionSchema') { $hasRoot = $true }
}
if (-not $hasRoot) {
$el = New-Object System.Xml.Schema.XmlSchemaElement
$el.Name = 'DataCompositionSchema'
$el.SchemaTypeName = New-Object System.Xml.XmlQualifiedName('DataCompositionSchema', $xsdDcsNs)
[void]$dcs.Items.Add($el)
}
$set = New-Object System.Xml.Schema.XmlSchemaSet
$set.XmlResolver = $null
foreach ($sc in $schemas) { [void]$set.Add($sc) }
try { $set.Compile() } catch { Report-Warn "XSD: schemas in '$useDir' do not compile — not checked: $($_.Exception.Message)"; return }
# Своя цепочка открытых элементов: в обработчике нужен родитель нарушителя
$script:xsdStack = New-Object System.Collections.ArrayList
$script:xsdFound = 0
$script:xsdTag = if ($exact) { "XSD $useVer" } else { "XSD $useVer (no schemas for $ver)" }
$script:xsdExact = $exact
$handler = {
param($sender, $e)
if ($script:stopped) { return }
$st = $script:xsdStack
if ($sender.NodeType -eq 'Element') {
$xt = $sender.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
$info = @{ elem = $sender.LocalName; parent = $(if ($st.Count -gt 0) { $st[$st.Count - 1].name } else { '' }); xsiNs = ''; xsiLocal = '' }
if ($xt) {
$pp = $xt.Split(':')
$info.xsiLocal = $pp[-1]
if ($pp.Count -gt 1) { $info.xsiNs = "$($sender.LookupNamespace($pp[0]))" }
}
} else {
$top = if ($st.Count -gt 0) { $st[$st.Count - 1] } else { @{ name = ''; xsiNs = ''; xsiLocal = '' } }
$info = @{ elem = $top.name; parent = $(if ($st.Count -gt 1) { $st[$st.Count - 2].name } else { '' }); xsiNs = $top.xsiNs; xsiLocal = $top.xsiLocal }
}
if (Test-XsdPlatformNoise $info) { return }
$script:xsdFound++
$msg = "$($script:xsdTag), line $($e.Exception.LineNumber): $($info.parent)/$($info.elem): $($e.Message -replace '[\r\n]+', ' ')"
if ($script:xsdExact) { Report-Error $msg } else { Report-Warn $msg }
}
$rs = New-Object System.Xml.XmlReaderSettings
$rs.ValidationType = 'Schema'
$rs.Schemas = $set
$rs.DtdProcessing = 'Prohibit'
$rs.add_ValidationEventHandler($handler)
$r = [System.Xml.XmlReader]::Create($resolvedPath, $rs)
try {
while ($r.Read()) {
if ($r.NodeType -eq 'Element' -and -not $r.IsEmptyElement) {
$xt = $r.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
$entry = @{ name = $r.LocalName; xsiNs = ''; xsiLocal = '' }
if ($xt) {
$pp = $xt.Split(':')
$entry.xsiLocal = $pp[-1]
if ($pp.Count -gt 1) { $entry.xsiNs = "$($r.LookupNamespace($pp[0]))" }
}
[void]$script:xsdStack.Add($entry)
} elseif ($r.NodeType -eq 'EndElement' -and $script:xsdStack.Count -gt 0) {
$script:xsdStack.RemoveAt($script:xsdStack.Count - 1)
}
if ($script:stopped) { break }
}
} finally { $r.Dispose() }
if ($script:xsdFound -eq 0) { Report-OK "$($script:xsdTag): order, types and values OK" }
}
Invoke-XsdCheck
if ($script:stopped) { & $finalize; exit 1 }
# --- Final output --- # --- Final output ---
& $finalize & $finalize
@@ -1,4 +1,4 @@
# skd-validate v1.4 — Validate 1C DCS structure (Python port) # skd-validate v1.5 — Validate 1C DCS structure (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import os import os
@@ -881,6 +881,290 @@ for vn in value_nodes:
if v_checked > 0 and v_ok: if v_checked > 0 and v_ok:
report_ok(f"{v_checked} <value> element(s) with xsi:type: content OK") report_ok(f"{v_checked} <value> element(s) with xsi:type: content OK")
if stopped:
finalize()
sys.exit(1)
# ── 18. XSD (schemas of the platform, /v8-xsd-fetch) ──────────
# Порядок элементов, типы и значения — по XSD платформы, если они есть в проекте. Нет схем —
# нет проверки. Схемы XDTO совпадают с выгрузкой конфигуратора только в СКД, и то кроме
# нескольких мест, которые платформа пишет иначе, — они отсекаются ниже (xsd_platform_noise).
import json
import re
import shutil
import tempfile
from pathlib import Path
def _sg_find_v8project(start_dir):
d = start_dir
for _ in range(20):
if not d:
break
pj = os.path.join(d, ".v8-project.json")
if os.path.isfile(pj):
return pj
parent = os.path.dirname(d)
if parent == d:
break
d = parent
return None
# Штамп версии формата — атрибут version КОРНЕВОГО элемента файла. Именно корневого: в Form.xml
# расширения ниже стоит <BaseForm version=…>. Читается только заголовок, без разбора файла.
def root_version(xml_path):
if not os.path.isfile(xml_path):
return None
with open(xml_path, "rb") as f:
head = f.read(4096).decode("utf-8", errors="ignore")
m = re.search(r'<[A-Za-z_][\w.:-]*(\s[^>]*)?/?>', head)
if not m:
return None
v = re.search(r'(?:^|\s)version="([^"]*)"', m.group(1) or "")
if v:
return v.group(1)
return None
# Корень автономной внешней обработки/отчёта. Копия общего эталона (семья is_external_root,
# авторитет — cf-edit).
def _sg_is_external_root(xml_path):
if not os.path.isfile(xml_path):
return False
try:
mx = etree.parse(xml_path).getroot()
for child in mx:
if isinstance(child.tag, str):
return child.tag.split("}")[-1] in ("ExternalDataProcessor", "ExternalReport")
except Exception:
return False
return False
# Якорь выгрузки, в чьём дереве лежит файл: корень автономной EPF/ERF либо Configuration.xml,
# ближайший вверх. Корень обработки проверяется первым — иначе обработка, лежащая в дереве
# конфигурации, сверялась бы с конфигурацией. Нет якоря — сверять не с чем.
def find_dump_anchor(start_dir):
d = start_dir
for _ in range(15):
if not d:
break
if _sg_is_external_root(d + ".xml"):
return d + ".xml"
cfg = os.path.join(d, "Configuration.xml")
if os.path.exists(cfg):
return cfg
parent = os.path.dirname(d)
if not parent or parent == d:
break
d = parent
return None
def format_rank(ver):
""""2.20" → 220, "2.9" → 209. Строковое сравнение неверно ("2.9" > "2.17")."""
m = re.match(r'^(\d+)\.(\d+)$', ver or '')
return int(m.group(1)) * 100 + int(m.group(2)) if m else 0
XSD_DCS_NS = "http://v8.1c.ru/8.1/data-composition-system/schema"
XS_NS = "http://www.w3.org/2001/XMLSchema"
def xsd_target_ns(path):
"""targetNamespace схемы — из заголовка файла, без разбора."""
with open(path, "rb") as f:
head = f.read(4096).decode("utf-8", errors="ignore")
m = re.search(r'targetNamespace="([^"]*)"', head)
return m.group(1) if m else None
def xsd_index(vdir):
"""namespace → файл схемы в каталоге версии."""
idx = {}
for name in sorted(os.listdir(vdir)):
fp = os.path.join(vdir, name)
if name.lower().endswith(".xsd") and os.path.isfile(fp):
ns = xsd_target_ns(fp)
if ns and ns not in idx:
idx[ns] = fp
return idx
def resolve_xsd_root(template_dir):
"""Каталог схем: .v8-project.json — сначала от шаблона (проект, которому он принадлежит),
потом от текущего каталога; xsdPath из него, иначе .v8-xsd рядом с ним. Нет проекта — нет схем."""
pj = _sg_find_v8project(template_dir) or _sg_find_v8project(os.getcwd())
if not pj:
return None
pj_dir = os.path.dirname(pj)
rel = ".v8-xsd"
try:
with open(pj, encoding="utf-8-sig") as f:
cfg = json.load(f)
if isinstance(cfg, dict) and cfg.get("xsdPath"):
rel = str(cfg["xsdPath"])
except Exception:
pass
d = rel if os.path.isabs(rel) else os.path.join(pj_dir, rel)
return d if os.path.isdir(d) else None
def xsd_platform_noise(info):
"""Места, которые платформа пишет не так, как описывает XSD, — сверено на всех схемах
компоновки выгрузок БП и УНФ. Правило — по месту (элемент, родитель, xsi:type), не по тексту:
текст зависит от движка и языка."""
e, p = info["elem"], info["parent"]
if p in ("groupTemplate", "groupHeaderTemplate") and e in ("templateType", "groupName"):
return True
if p == "right" and e == "lastId":
return True
if p == "nestedSchema" and e == "schema":
return True
if info["xsiNs"] == "http://v8.1c.ru/8.2/data/chart":
return True
# Пустое выражение параметра макета платформа не пишет, схема требует
if e == "parameter" and info["xsiLocal"] == "ExpressionAreaTemplateParameter":
return True
return False
def _xsd_build_schema(vdir, tmp):
"""Схема для lxml. У import в схемах платформы нет schemaLocation, а libxml2 сам по namespace
не ищет, — корневая схема импортирует замыкание зависимостей СКД с путями, листья первыми.
Корень DataCompositionSchema в схеме объявлен как dataCompositionSchema (строчная) — схему
СКД подключаем через обёртку: xs:include настоящего файла плюс элемент с нужным именем."""
idx = xsd_index(vdir)
order, seen = [], set()
def visit(ns):
if ns in seen or ns not in idx:
return
seen.add(ns)
doc = etree.parse(idx[ns])
for imp in doc.getroot().iter("{%s}import" % XS_NS):
visit(imp.get("namespace"))
order.append(ns)
# Сначала замыкание СКД, затем остальные схемы каталога: значения в выгрузке ссылаются через
# xsi:type на типы, которые схема СКД не импортирует (data/enterprise: LinkedValueChangeMode,
# FoldersAndItemsUse …) — как и в PS-мастере, где в набор кладутся все схемы.
visit(XSD_DCS_NS)
for ns in idx:
visit(ns)
dcs_doc = etree.parse(idx[XSD_DCS_NS])
has_root = any(el.get("name") == "DataCompositionSchema"
for el in dcs_doc.getroot().findall("{%s}element" % XS_NS))
locations = {ns: Path(idx[ns]).as_uri() for ns in order}
if not has_root:
wrapper = os.path.join(tmp, "dcs-root.xsd")
with open(wrapper, "w", encoding="utf-8") as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n'
'<xs:schema xmlns:xs="%s" xmlns:tns="%s" targetNamespace="%s" elementFormDefault="qualified">\n'
' <xs:include schemaLocation="%s"/>\n'
' <xs:element name="DataCompositionSchema" type="tns:DataCompositionSchema"/>\n'
'</xs:schema>\n' % (XS_NS, XSD_DCS_NS, XSD_DCS_NS, locations[XSD_DCS_NS]))
locations[XSD_DCS_NS] = Path(wrapper).as_uri()
root = os.path.join(tmp, "root.xsd")
with open(root, "w", encoding="utf-8") as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n<xs:schema xmlns:xs="%s" targetNamespace="urn:v8-xsd-root">\n' % XS_NS)
for ns in order:
f.write(' <xs:import namespace="%s" schemaLocation="%s"/>\n' % (ns, locations[ns]))
f.write('</xs:schema>\n')
return etree.XMLSchema(etree.parse(root))
def _xsd_elem_info(el):
if el is None or not isinstance(el.tag, str):
return {"elem": "", "parent": "", "xsiNs": "", "xsiLocal": ""}
par = el.getparent()
info = {"elem": etree.QName(el).localname,
"parent": etree.QName(par).localname if par is not None and isinstance(par.tag, str) else "",
"xsiNs": "", "xsiLocal": ""}
xt = el.get(XSI_TYPE)
if xt:
pp = xt.split(":")
info["xsiLocal"] = pp[-1]
if len(pp) > 1:
info["xsiNs"] = el.nsmap.get(pp[0]) or ""
return info
def _xsd_error_element(err):
"""Элемент нарушения по error.path. Путь несёт префиксы документа (dcsset:settings) — xpath
получает их из корня; префикс, объявленный глубже, не разрешится — тогда ищем по строке и
имени последнего шага."""
if not err.path:
return None
ns = {k: v for k, v in tree.getroot().nsmap.items() if k}
try:
hits = tree.xpath(err.path, namespaces=ns)
if hits and isinstance(hits[0], etree._Element):
return hits[0]
except Exception:
pass
last = re.sub(r'\[\d+\]$', '', err.path.rsplit('/', 1)[-1]).split(':')[-1]
for el in tree.iter():
if isinstance(el.tag, str) and el.sourceline == err.line and etree.QName(el).localname == last:
return el
return None
def xsd_check():
xsd_root = resolve_xsd_root(os.path.dirname(resolved_path))
if not xsd_root:
report_ok("XSD: no schemas in project — not checked")
return
anchor = find_dump_anchor(os.path.dirname(resolved_path))
ver = root_version(anchor) if anchor else None
if not ver or format_rank(ver) == 0:
report_ok("XSD: format version unknown (template outside a dump) — not checked")
return
# Точная версия — ошибки; ближайшая более новая — предупреждения; только старше — пропуск
cands = sorted((d for d in os.listdir(xsd_root)
if re.match(r'^\d+\.\d+$', d) and os.path.isdir(os.path.join(xsd_root, d))
and format_rank(d) >= format_rank(ver)), key=format_rank)
cands = [d for d in cands if XSD_DCS_NS in xsd_index(os.path.join(xsd_root, d))]
if not cands:
report_warn(f"XSD: no schemas for format {ver} in '{xsd_root}' — not checked")
return
use_ver = cands[0]
exact = use_ver == ver
tmp = tempfile.mkdtemp(prefix="skd-xsd-")
try:
try:
schema = _xsd_build_schema(os.path.join(xsd_root, use_ver), tmp)
except Exception as ex:
report_warn(f"XSD: schemas in '{os.path.join(xsd_root, use_ver)}' do not compile — not checked: {ex}")
return
finally:
shutil.rmtree(tmp, ignore_errors=True)
tag = f"XSD {use_ver}" if exact else f"XSD {use_ver} (no schemas for {ver})"
schema.validate(tree)
found = 0
for err in schema.error_log:
if stopped:
break
el = _xsd_error_element(err)
info = _xsd_elem_info(el)
if xsd_platform_noise(info):
continue
found += 1
msg = f"{tag}, line {err.line}: {info['parent']}/{info['elem']}: {' '.join(err.message.split())}"
if exact:
report_error(msg)
else:
report_warn(msg)
if found == 0:
report_ok(f"{tag}: order, types and values OK")
xsd_check()
if stopped: if stopped:
finalize() finalize()
sys.exit(1) sys.exit(1)
+4
View File
@@ -268,6 +268,10 @@
[yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest). Схемы — [yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest). Схемы —
собственность ООО «1С-Софт», в Git их не добавляют. Нет схем — нет проверки. собственность ООО «1С-Софт», в Git их не добавляют. Нет схем — нет проверки.
Схемы читает `/skd-validate`: порядок элементов, типы и значения схемы компоновки — по схемам
версии формата выгрузки (атрибут `version` в `Configuration.xml`). Для объектов метаданных и форм
схемы XDTO не совпадают с форматом выгрузки конфигуратора, поэтому другие валидаторы их не используют.
### `webUrl` — URL веб-клиента (per-database) ### `webUrl` — URL веб-клиента (per-database)
URL для открытия базы в браузере через `/web-test`. Задаётся в записи конкретной базы. URL для открытия базы в браузере через `/web-test`. Задаётся в записи конкретной базы.
+3 -1
View File
@@ -24,7 +24,8 @@ Exit code: 0 = все прошли, 1 = есть падения.
### Сколько кейсов должно быть пропущено ### Сколько кейсов должно быть пропущено
`○ skipped` — норма, а не падение: кейс может гейтиться по ОС (`osOnly`), по порту `○ skipped` — норма, а не падение: кейс может гейтиться по ОС (`osOnly`), по порту
(`runtimeOnly`) или требовать внешней выгрузки (`setup: external:`), которой на машине нет. (`runtimeOnly`), требовать внешней выгрузки (`setup: external:`), которой на машине нет, или
локальных данных вне репозитория (`requires`, например XSD-схемы 1С в `.v8-xsd`).
Но само число ни о чём не говорит, пока не с чем сверить, а запоминать его нельзя — оно растёт Но само число ни о чём не говорит, пока не с чем сверить, а запоминать его нельзя — оно растёт
с набором кейсов. с набором кейсов.
@@ -402,6 +403,7 @@ ibcmd-проход автоматически `○ skipped`, если рядом
| `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») | | `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») |
| `idempotent` | нет | `true` — повторный прогон с теми же аргументами должен дать байт-в-байт тот же `workDir` | | `idempotent` | нет | `true` — повторный прогон с теми же аргументами должен дать байт-в-байт тот же `workDir` |
| `runtimeOnly` | нет | `"powershell"` / `"python"` — кейс имеет смысл только на одном порте, на другом `○ skipped` | | `runtimeOnly` | нет | `"powershell"` / `"python"` — кейс имеет смысл только на одном порте, на другом `○ skipped` |
| `requires` | нет | Путь или массив путей от корня репозитория, без которых кейс не имеет смысла (например `".v8-xsd/2.20"` — схемы 1С не входят в репозиторий). Нет пути — `○ skipped`. В `writeFile` путь к корню — плейсхолдер `{repoRoot}` |
| `osOnly` | нет | `"win32"` / `"darwin"` / `"linux"` — кейс работает только на этой ОС, на других `○ skipped`. Принимает и массив: `["darwin","linux"]`. Для фейка платформы **не нужен**: см. `fakePlatform` | | `osOnly` | нет | `"win32"` / `"darwin"` / `"linux"` — кейс работает только на этой ОС, на других `○ skipped`. Принимает и массив: `["darwin","linux"]`. Для фейка платформы **не нужен**: см. `fakePlatform` |
| `fakePlatform` | нет | `{ "log": "…", "exit": 0 }` — кейсу нужна фейковая платформа (см. ниже) | | `fakePlatform` | нет | `{ "log": "…", "exit": 0 }` — кейсу нужна фейковая платформа (см. ниже) |
| `cwd` | нет | `"workDir"` — запустить навык из рабочего каталога (нужно, если кейс кладёт туда `.v8-project.json`) | | `cwd` | нет | `"workDir"` — запустить навык из рабочего каталога (нужно, если кейс кладёт туда `.v8-project.json`) |
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" version="2.11">
<Configuration uuid="00000000-0000-0000-0000-000000000001"/>
</MetaDataObject>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataSource>
<name>ИсточникДанных1</name>
<dataSourceType>Local</dataSourceType>
</dataSource>
<dataSet xsi:type="DataSetQuery">
<name>НаборДанных1</name>
<field xsi:type="DataSetFieldField">
<dataPath>Число</dataPath>
<field>Число</field>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<presentationExpression>Формат(Число, "ЧГ=")</presentationExpression>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Флаг</dataPath>
<field>Флаг</field>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
</field>
<dataSource>ИсточникДанных1</dataSource>
<query>ВЫБРАТЬ 1 КАК Число, ИСТИНА КАК Флаг</query>
</dataSet>
<calculatedField>
<dataPath>Удвоенное</dataPath>
<expression>Число * 2</expression>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<useRestriction>
<group>true</group>
</useRestriction>
</calculatedField>
<parameter>
<name>П</name>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<value xsi:type="xs:boolean">False</value>
<useRestriction>false</useRestriction>
</parameter>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Основной</v8:content>
</v8:item>
</dcsset:presentation>
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Число</dcsset:field>
</dcsset:item>
</dcsset:selection>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Флаг</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:right xsi:type="xs:boolean">true</dcsset:right>
</dcsset:item>
</dcsset:filter>
<dcsset:outputParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Заголовок</dcscor:parameter>
<dcscor:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Пример</v8:content>
</v8:item>
</dcscor:value>
</dcscor:item>
</dcsset:outputParameters>
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>П</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">false</dcscor:value>
<dcsset:userSettingID>0930043f-cf2a-42c1-ae8c-4accf2fd2604</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
</dcsset:item>
</dcsset:settings>
</settingsVariant>
</DataCompositionSchema>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" version="2.20">
<Configuration uuid="00000000-0000-0000-0000-000000000001"/>
</MetaDataObject>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataSource>
<name>ИсточникДанных1</name>
<dataSourceType>Local</dataSourceType>
</dataSource>
<dataSet xsi:type="DataSetQuery">
<name>НаборДанных1</name>
<field xsi:type="DataSetFieldField">
<dataPath>Число</dataPath>
<field>Число</field>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<presentationExpression>Формат(Число, "ЧГ=")</presentationExpression>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Флаг</dataPath>
<field>Флаг</field>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
</field>
<dataSource>ИсточникДанных1</dataSource>
<query>ВЫБРАТЬ 1 КАК Число, ИСТИНА КАК Флаг</query>
</dataSet>
<calculatedField>
<dataPath>Удвоенное</dataPath>
<expression>Число * 2</expression>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<useRestriction>
<group>true</group>
</useRestriction>
</calculatedField>
<parameter>
<name>П</name>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<value xsi:type="xs:boolean">False</value>
<useRestriction>false</useRestriction>
</parameter>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Основной</v8:content>
</v8:item>
</dcsset:presentation>
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Число</dcsset:field>
</dcsset:item>
</dcsset:selection>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Флаг</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:right xsi:type="xs:boolean">true</dcsset:right>
</dcsset:item>
</dcsset:filter>
<dcsset:outputParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Заголовок</dcscor:parameter>
<dcscor:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Пример</v8:content>
</v8:item>
</dcscor:value>
</dcscor:item>
</dcsset:outputParameters>
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>П</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">false</dcscor:value>
<dcsset:userSettingID>0930043f-cf2a-42c1-ae8c-4accf2fd2604</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
</dcsset:item>
</dcsset:settings>
</settingsVariant>
</DataCompositionSchema>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" version="2.20">
<Configuration uuid="00000000-0000-0000-0000-000000000001"/>
</MetaDataObject>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataSource>
<name>ИсточникДанных1</name>
<dataSourceType>Local</dataSourceType>
</dataSource>
<dataSet xsi:type="DataSetQuery">
<name>НаборДанных1</name>
<field xsi:type="DataSetFieldField">
<dataPath>Число</dataPath>
<field>Число</field>
<presentationExpression>Формат(Число, "ЧГ=")</presentationExpression>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Флаг</dataPath>
<field>Флаг</field>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
</field>
<dataSource>ИсточникДанных1</dataSource>
<query>ВЫБРАТЬ 1 КАК Число, ИСТИНА КАК Флаг</query>
</dataSet>
<calculatedField>
<dataPath>Удвоенное</dataPath>
<expression>Число * 2</expression>
<useRestriction>
<group>true</group>
</useRestriction>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</calculatedField>
<parameter>
<name>П</name>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<value xsi:type="xs:boolean">false</value>
<useRestriction>false</useRestriction>
</parameter>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Основной</v8:content>
</v8:item>
</dcsset:presentation>
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Число</dcsset:field>
</dcsset:item>
</dcsset:selection>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Флаг</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:right xsi:type="xs:boolean">true</dcsset:right>
</dcsset:item>
</dcsset:filter>
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>П</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">false</dcscor:value>
<dcsset:userSettingID>0eb70b37-3a01-40a0-81c1-98028ef06a05</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:outputParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Заголовок</dcscor:parameter>
<dcscor:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Пример</v8:content>
</v8:item>
</dcscor:value>
</dcscor:item>
</dcsset:outputParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
</dcsset:item>
</dcsset:settings>
</settingsVariant>
</DataCompositionSchema>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataSource>
<name>ИсточникДанных1</name>
<dataSourceType>Local</dataSourceType>
</dataSource>
<dataSet xsi:type="DataSetQuery">
<name>НаборДанных1</name>
<field xsi:type="DataSetFieldField">
<dataPath>Число</dataPath>
<field>Число</field>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<presentationExpression>Формат(Число, "ЧГ=")</presentationExpression>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Флаг</dataPath>
<field>Флаг</field>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
</field>
<dataSource>ИсточникДанных1</dataSource>
<query>ВЫБРАТЬ 1 КАК Число, ИСТИНА КАК Флаг</query>
</dataSet>
<calculatedField>
<dataPath>Удвоенное</dataPath>
<expression>Число * 2</expression>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<useRestriction>
<group>true</group>
</useRestriction>
</calculatedField>
<parameter>
<name>П</name>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<value xsi:type="xs:boolean">False</value>
<useRestriction>false</useRestriction>
</parameter>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Основной</v8:content>
</v8:item>
</dcsset:presentation>
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Число</dcsset:field>
</dcsset:item>
</dcsset:selection>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Флаг</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:right xsi:type="xs:boolean">true</dcsset:right>
</dcsset:item>
</dcsset:filter>
<dcsset:outputParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Заголовок</dcscor:parameter>
<dcscor:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Пример</v8:content>
</v8:item>
</dcscor:value>
</dcscor:item>
</dcsset:outputParameters>
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>П</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">false</dcscor:value>
<dcsset:userSettingID>0930043f-cf2a-42c1-ae8c-4accf2fd2604</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
</dcsset:item>
</dcsset:settings>
</settingsVariant>
</DataCompositionSchema>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" version="2.20">
<Configuration uuid="00000000-0000-0000-0000-000000000001"/>
</MetaDataObject>
@@ -0,0 +1,407 @@
<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dataSource>
<name>ИсточникДанных1</name>
<dataSourceType>Local</dataSourceType>
</dataSource>
<dataSet xsi:type="DataSetQuery">
<name>Тест</name>
<field xsi:type="DataSetFieldField">
<dataPath>Организация</dataPath>
<field>Организация</field>
<role>
<dcscom:dimension>true</dcscom:dimension>
</role>
<valueType>
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Организации</v8:Type>
</valueType>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Номенклатура</dataPath>
<field>Номенклатура</field>
<role>
<dcscom:dimension>true</dcscom:dimension>
</role>
<valueType>
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Номенклатура</v8:Type>
</valueType>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Сумма</dataPath>
<field>Сумма</field>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Количество</dataPath>
<field>Количество</field>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</field>
<field xsi:type="DataSetFieldField">
<dataPath>Статус</dataPath>
<field>Статус</field>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
</field>
<dataSource>ИсточникДанных1</dataSource>
<query>ВЫБРАТЬ * ИЗ Справочник.Номенклатура</query>
</dataSet>
<parameter>
<name>Период</name>
<valueType>
<v8:Type>v8:StandardPeriod</v8:Type>
</valueType>
<value xsi:type="v8:StandardPeriod">
<v8:variant xsi:type="v8:StandardPeriodVariant">LastMonth</v8:variant>
</value>
<useRestriction>false</useRestriction>
<denyIncompleteValues>true</denyIncompleteValues>
<use>Always</use>
</parameter>
<parameter>
<name>НачалоПериода</name>
<title xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Начало периода</v8:content>
</v8:item>
</title>
<valueType>
<v8:Type>xs:dateTime</v8:Type>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<value xsi:type="xs:dateTime">0001-01-01T00:00:00</value>
<useRestriction>true</useRestriction>
<expression>&amp;Период.ДатаНачала</expression>
</parameter>
<parameter>
<name>КонецПериода</name>
<title xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Конец периода</v8:content>
</v8:item>
</title>
<valueType>
<v8:Type>xs:dateTime</v8:Type>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<value xsi:type="xs:dateTime">0001-01-01T00:00:00</value>
<useRestriction>true</useRestriction>
<expression>&amp;Период.ДатаОкончания</expression>
</parameter>
<parameter>
<name>Активные</name>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<value xsi:type="xs:boolean">true</value>
<useRestriction>false</useRestriction>
</parameter>
<template>
<name>Шапка</name>
<template xmlns:dcsat="http://v8.1c.ru/8.1/data-composition-system/area-template" xsi:type="dcsat:AreaTemplate">
<dcsat:item xsi:type="dcsat:TableRow">
<dcsat:tableCell>
<dcsat:item xsi:type="dcsat:Field">
<dcsat:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Орг</v8:content>
</v8:item>
</dcsat:value>
</dcsat:item>
<dcsat:appearance>
<dcscor:item>
<dcscor:parameter>ЦветФона</dcscor:parameter>
<dcscor:value xmlns:d8p1="http://v8.1c.ru/8.1/data/ui/style" xsi:type="v8ui:Color">d8p1:ReportHeaderBackColor</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>ЦветГраницы</dcscor:parameter>
<dcscor:value xmlns:d8p1="http://v8.1c.ru/8.1/data/ui/style" xsi:type="v8ui:Color">d8p1:ReportLineColor</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="0" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">None</v8ui:style>
</dcscor:value>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Слева</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Сверху</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Справа</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Снизу</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>Шрифт</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Font" faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>ГоризонтальноеПоложение</dcscor:parameter>
<dcscor:value xsi:type="v8ui:HorizontalAlign">Center</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>Размещение</dcscor:parameter>
<dcscor:value xsi:type="dcscor:DataCompositionTextPlacementType">Wrap</dcscor:value>
</dcscor:item>
</dcsat:appearance>
</dcsat:tableCell>
<dcsat:tableCell>
<dcsat:item xsi:type="dcsat:Field">
<dcsat:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сум</v8:content>
</v8:item>
</dcsat:value>
</dcsat:item>
<dcsat:appearance>
<dcscor:item>
<dcscor:parameter>ЦветФона</dcscor:parameter>
<dcscor:value xmlns:d8p1="http://v8.1c.ru/8.1/data/ui/style" xsi:type="v8ui:Color">d8p1:ReportHeaderBackColor</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>ЦветГраницы</dcscor:parameter>
<dcscor:value xmlns:d8p1="http://v8.1c.ru/8.1/data/ui/style" xsi:type="v8ui:Color">d8p1:ReportLineColor</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="0" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">None</v8ui:style>
</dcscor:value>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Слева</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Сверху</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Справа</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>СтильГраницы.Снизу</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Line" width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</dcscor:value>
</dcscor:item>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>Шрифт</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Font" faceName="Arial" height="10" bold="false" italic="false" underline="false" strikeout="false" kind="Absolute" scale="100"/>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>ГоризонтальноеПоложение</dcscor:parameter>
<dcscor:value xsi:type="v8ui:HorizontalAlign">Center</dcscor:value>
</dcscor:item>
<dcscor:item>
<dcscor:parameter>Размещение</dcscor:parameter>
<dcscor:value xsi:type="dcscor:DataCompositionTextPlacementType">Wrap</dcscor:value>
</dcscor:item>
</dcsat:appearance>
</dcsat:tableCell>
</dcsat:item>
</template>
</template>
<groupHeaderTemplate>
<groupName>ДанныеОтчета</groupName>
<templateType>Header</templateType>
<template>Шапка</template>
</groupHeaderTemplate>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Основной вариант</v8:content>
</v8:item>
</dcsset:presentation>
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Организация</dcsset:field>
</dcsset:item>
<dcsset:item xsi:type="dcsset:SelectedItemFolder">
<dcsset:lwsTitle>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Объёмы</v8:content>
</v8:item>
</dcsset:lwsTitle>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Сумма</dcsset:field>
</dcsset:item>
<dcsset:item xsi:type="dcsset:SelectedItemField">
<dcsset:field>Количество</dcsset:field>
</dcsset:item>
<dcsset:placement>Auto</dcsset:placement>
</dcsset:item>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:use>false</dcsset:use>
<dcsset:left xsi:type="dcscor:Field">Организация</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:userSettingID>UUID-001</dcsset:userSettingID>
</dcsset:item>
<dcsset:item xsi:type="dcsset:FilterItemGroup">
<dcsset:groupType>OrGroup</dcsset:groupType>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Статус</dcsset:left>
<dcsset:comparisonType>Equal</dcsset:comparisonType>
<dcsset:right xsi:type="xs:string">Активен</dcsset:right>
</dcsset:item>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Сумма</dcsset:left>
<dcsset:comparisonType>Greater</dcsset:comparisonType>
<dcsset:right xsi:type="xs:decimal">1000</dcsset:right>
</dcsset:item>
</dcsset:item>
</dcsset:filter>
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Период</dcscor:parameter>
<dcscor:value xsi:type="v8:StandardPeriod">
<v8:variant xsi:type="v8:StandardPeriodVariant">LastMonth</v8:variant>
</dcscor:value>
<dcsset:userSettingID>UUID-002</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Активные</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">true</dcscor:value>
<dcsset:userSettingID>UUID-003</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemField">
<dcsset:field>Сумма</dcsset:field>
<dcsset:orderType>Desc</dcsset:orderType>
</dcsset:item>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:conditionalAppearance>
<dcsset:item>
<dcsset:selection/>
<dcsset:filter>
<dcsset:item xsi:type="dcsset:FilterItemComparison">
<dcsset:left xsi:type="dcscor:Field">Сумма</dcsset:left>
<dcsset:comparisonType>Greater</dcsset:comparisonType>
<dcsset:right xsi:type="xs:decimal">10000</dcsset:right>
</dcsset:item>
</dcsset:filter>
<dcsset:appearance>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>ЦветТекста</dcscor:parameter>
<dcscor:value xsi:type="v8ui:Color">style:НегативныйТекстЦвет</dcscor:value>
</dcscor:item>
</dcsset:appearance>
<dcsset:presentation xsi:type="xs:string">Большие суммы</dcsset:presentation>
<dcsset:viewMode>Normal</dcsset:viewMode>
<dcsset:userSettingID>UUID-004</dcsset:userSettingID>
</dcsset:item>
</dcsset:conditionalAppearance>
<dcsset:outputParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Заголовок</dcscor:parameter>
<dcscor:value xsi:type="v8:LocalStringType">
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сводка по организациям</v8:content>
</v8:item>
</dcscor:value>
</dcscor:item>
</dcsset:outputParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:groupItems>
<dcsset:item xsi:type="dcsset:GroupItemField">
<dcsset:field>Организация</dcsset:field>
<dcsset:groupType>Items</dcsset:groupType>
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
</dcsset:item>
</dcsset:groupItems>
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:groupItems>
<dcsset:item xsi:type="dcsset:GroupItemField">
<dcsset:field>Номенклатура</dcsset:field>
<dcsset:groupType>Items</dcsset:groupType>
<dcsset:periodAdditionType>None</dcsset:periodAdditionType>
<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>
<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>
</dcsset:item>
</dcsset:groupItems>
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
</dcsset:order>
<dcsset:selection>
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
</dcsset:selection>
</dcsset:item>
</dcsset:item>
</dcsset:item>
</dcsset:settings>
</settingsVariant>
</DataCompositionSchema>
@@ -0,0 +1,28 @@
{
"name": "XSD точной версии: нарушенный порядок и False — ошибки с местом нарушения",
"setup": "fixture:xsd-bad-220",
"requires": ".v8-xsd/2.20",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "{repoRoot}/.v8-xsd"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"expectError": true,
"expect": {
"stdoutContains": [
"[ERROR] XSD 2.20, line 20: field/presentationExpression",
"[ERROR] XSD 2.20, line 43: calculatedField/useRestriction",
"[ERROR] XSD 2.20, line 52: parameter/value",
"[ERROR] XSD 2.20, line 87: settings/dataParameters"
]
},
"noSnapshot": "результат — только вывод; схемы 1С не должны попасть в эталон"
}
@@ -0,0 +1,29 @@
{
"name": "XSD точной версии: правильная схема проходит без строк XSD",
"setup": "fixture:xsd-good-220",
"requires": ".v8-xsd/2.20",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "{repoRoot}/.v8-xsd"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"args_extra": [
"-Detailed"
],
"expect": {
"stdoutContains": "[OK] XSD 2.20: order, types and values OK",
"stdoutNotContains": [
"[ERROR]",
"[WARN]"
]
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,28 @@
{
"name": "Схем точной версии нет (2.11 без схемы СКД) — по ближайшей новой, нарушения предупреждениями",
"setup": "fixture:xsd-bad-211",
"requires": [
".v8-xsd/2.11",
".v8-xsd/2.12"
],
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "{repoRoot}/.v8-xsd"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"expect": {
"stdoutContains": [
"[WARN] XSD 2.12 (no schemas for 2.11), line 20: field/presentationExpression"
],
"stdoutNotContains": "[ERROR]"
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,34 @@
{
"name": "Шаблон вне выгрузки — версия формата неизвестна, проверка не выполняется",
"setup": "fixture:xsd-no-anchor",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "xsd"
}
}
},
{
"writeFile": {
"path": "xsd/2.20/README.txt",
"content": "каталог версии"
}
}
],
"params": {
"templatePath": "Template.xml"
},
"args_extra": [
"-Detailed"
],
"expect": {
"stdoutContains": "[OK] XSD: format version unknown (template outside a dump) — not checked",
"stdoutNotContains": [
"[ERROR]",
"[WARN]"
]
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,28 @@
{
"name": "Каталога схем нет, -Detailed — строка о пропуске",
"setup": "fixture:xsd-bad-220",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "нет-такого-каталога"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"args_extra": [
"-Detailed"
],
"expect": {
"stdoutContains": "[OK] XSD: no schemas in project — not checked",
"stdoutNotContains": [
"[ERROR]",
"[WARN]"
]
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,22 @@
{
"name": "Каталога схем нет — проверки нет, строк XSD в выводе нет",
"setup": "fixture:xsd-bad-220",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "нет-такого-каталога"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"expect": {
"stdoutContains": "=== Validation OK",
"stdoutNotContains": "XSD"
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,28 @@
{
"name": "В каталоге схем только старшие версии — предупреждение, проверка не выполняется",
"setup": "fixture:xsd-bad-220",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "xsd"
}
}
},
{
"writeFile": {
"path": "xsd/2.10/README.txt",
"content": "нет схемы СКД"
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"expect": {
"stdoutContains": "[WARN] XSD: no schemas for format 2.20 in",
"stdoutNotContains": "[ERROR]"
},
"noSnapshot": "результат — только вывод"
}
@@ -0,0 +1,29 @@
{
"name": "Места, которые платформа пишет иначе, чем XSD (templateType в groupHeaderTemplate), — не ошибка",
"setup": "fixture:xsd-noise-220",
"requires": ".v8-xsd/2.20",
"preRun": [
{
"writeFile": {
"path": ".v8-project.json",
"content": {
"xsdPath": "{repoRoot}/.v8-xsd"
}
}
}
],
"params": {
"templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml"
},
"args_extra": [
"-Detailed"
],
"expect": {
"stdoutContains": "[OK] XSD 2.20: order, types and values OK",
"stdoutNotContains": [
"[ERROR]",
"[WARN]"
]
},
"noSnapshot": "результат — только вывод"
}
+5 -5
View File
@@ -81,7 +81,7 @@ const FAMILIES = [
'form-info', 'meta-info', 'mxl-info', 'role-info', 'skd-info', 'subsystem-info', 'form-info', 'meta-info', 'mxl-info', 'role-info', 'skd-info', 'subsystem-info',
// Валидаторы отличают автономную обработку от конфигурации: тот же корень — якорь выгрузки // Валидаторы отличают автономную обработку от конфигурации: тот же корень — якорь выгрузки
// для сверки штампа версии (find_dump_anchor) // для сверки штампа версии (find_dump_anchor)
'form-validate', 'interface-validate', 'meta-validate', 'role-validate', 'subsystem-validate'] }, 'form-validate', 'interface-validate', 'meta-validate', 'role-validate', 'skd-validate', 'subsystem-validate'] },
], ],
}, },
{ {
@@ -93,7 +93,7 @@ const FAMILIES = [
{ id: 'full', authority: 'cf-edit', { id: 'full', authority: 'cf-edit',
consumers: ['cfe-borrow', 'db-cfe-admin', 'db-create', 'db-dump-cf', 'db-dump-dt', 'db-dump-xml', consumers: ['cfe-borrow', 'db-cfe-admin', 'db-create', 'db-dump-cf', 'db-dump-dt', 'db-dump-xml',
'db-load-cf', 'db-load-dt', 'db-load-git', 'db-load-xml', 'db-repo', 'db-run', 'db-update', 'db-load-cf', 'db-load-dt', 'db-load-git', 'db-load-xml', 'db-repo', 'db-run', 'db-update',
'epf-build', 'epf-dump', 'web-publish', 'v8-xsd-fetch', 'epf-build', 'epf-dump', 'web-publish', 'v8-xsd-fetch', 'skd-validate',
'form-add', 'form-compile', 'form-edit', 'help-add', 'interface-edit', 'meta-compile', 'form-add', 'form-compile', 'form-edit', 'help-add', 'interface-edit', 'meta-compile',
'meta-edit', 'meta-remove', 'mxl-compile', 'role-compile', 'role-edit', 'skd-compile', 'skd-edit', 'meta-edit', 'meta-remove', 'mxl-compile', 'role-compile', 'role-edit', 'skd-compile', 'skd-edit',
'subsystem-compile', 'subsystem-edit', 'template-add', 'xdto-compile', 'xdto-edit'] }, 'subsystem-compile', 'subsystem-edit', 'template-add', 'xdto-compile', 'xdto-edit'] },
@@ -130,13 +130,13 @@ const FAMILIES = [
{ {
name: 'root_version', py: 'root_version', ps1: 'Get-RootVersion', name: 'root_version', py: 'root_version', ps1: 'Get-RootVersion',
variants: [ variants: [
{ id: 'base', authority: 'meta-validate', consumers: ['cf-validate', 'cfe-validate', 'epf-validate', 'form-validate', 'interface-validate', 'role-validate', 'subsystem-validate'] }, { id: 'base', authority: 'meta-validate', consumers: ['cf-validate', 'cfe-validate', 'epf-validate', 'form-validate', 'interface-validate', 'role-validate', 'skd-validate', 'subsystem-validate'] },
], ],
}, },
{ {
name: 'find_dump_anchor', py: 'find_dump_anchor', ps1: 'Find-DumpAnchor', name: 'find_dump_anchor', py: 'find_dump_anchor', ps1: 'Find-DumpAnchor',
variants: [ variants: [
{ id: 'base', authority: 'meta-validate', consumers: ['form-validate', 'interface-validate', 'role-validate', 'subsystem-validate'] }, { id: 'base', authority: 'meta-validate', consumers: ['form-validate', 'interface-validate', 'role-validate', 'skd-validate', 'subsystem-validate'] },
], ],
}, },
{ {
@@ -151,7 +151,7 @@ const FAMILIES = [
{ id: 'base', authority: 'meta-compile', { id: 'base', authority: 'meta-compile',
consumers: ['cf-init', 'cf-validate', 'cfe-borrow', 'cfe-init', 'cfe-patch-method', 'cfe-validate', 'epf-build', consumers: ['cf-init', 'cf-validate', 'cfe-borrow', 'cfe-init', 'cfe-patch-method', 'cfe-validate', 'epf-build',
'epf-init', 'epf-validate', 'erf-init', 'form-add', 'form-compile', 'form-validate', 'epf-init', 'epf-validate', 'erf-init', 'form-add', 'form-compile', 'form-validate',
'meta-validate', 'mxl-compile', 'role-compile', 'role-edit', 'subsystem-compile', 'subsystem-edit', 'meta-validate', 'mxl-compile', 'role-compile', 'role-edit', 'skd-validate', 'subsystem-compile', 'subsystem-edit',
'template-add', 'xdto-compile'] }, 'template-add', 'xdto-compile'] },
], ],
}, },
+5 -3
View File
@@ -22,7 +22,7 @@ const runtime = argv.includes('--runtime') ? argv[argv.indexOf('--runtime') + 1]
const listMode = argv.includes('--list'); const listMode = argv.includes('--list');
const os = process.platform; const os = process.platform;
const reasons = { external: [], runtimeOnly: [], osOnly: [] }; const reasons = { external: [], runtimeOnly: [], osOnly: [], requires: [] };
for (const skill of readdirSync(CASES)) { for (const skill of readdirSync(CASES)) {
const dir = join(CASES, skill); const dir = join(CASES, skill);
@@ -38,6 +38,7 @@ for (const skill of readdirSync(CASES)) {
// Порядок совпадает с раннером: сначала ОС, потом порт, потом фикстура. // Порядок совпадает с раннером: сначала ОС, потом порт, потом фикстура.
if (c.osOnly && ![].concat(c.osOnly).includes(os)) { reasons.osOnly.push(id); continue; } if (c.osOnly && ![].concat(c.osOnly).includes(os)) { reasons.osOnly.push(id); continue; }
if (c.runtimeOnly && c.runtimeOnly !== runtime) { reasons.runtimeOnly.push(id); continue; } if (c.runtimeOnly && c.runtimeOnly !== runtime) { reasons.runtimeOnly.push(id); continue; }
if (c.requires && ![].concat(c.requires).every(p => existsSync(join(HERE, '..', '..', p)))) { reasons.requires.push(id); continue; }
const setup = String(c.setup || skillCfg.setup || ''); const setup = String(c.setup || skillCfg.setup || '');
if (setup.startsWith('external:') && !existsSync(setup.slice('external:'.length))) { if (setup.startsWith('external:') && !existsSync(setup.slice('external:'.length))) {
reasons.external.push(id); reasons.external.push(id);
@@ -45,7 +46,7 @@ for (const skill of readdirSync(CASES)) {
} }
} }
const total = reasons.external.length + reasons.runtimeOnly.length + reasons.osOnly.length; const total = reasons.external.length + reasons.runtimeOnly.length + reasons.osOnly.length + reasons.requires.length;
if (listMode) { if (listMode) {
for (const [key, ids] of Object.entries(reasons)) { for (const [key, ids] of Object.entries(reasons)) {
@@ -58,6 +59,7 @@ if (listMode) {
console.log( console.log(
`Ожидается пропущенных: ${total} [${os}, runtime: ${runtime}] ` + `Ожидается пропущенных: ${total} [${os}, runtime: ${runtime}] ` +
`= external ${reasons.external.length} + runtimeOnly ${reasons.runtimeOnly.length} + osOnly ${reasons.osOnly.length}`, `= external ${reasons.external.length} + runtimeOnly ${reasons.runtimeOnly.length} + osOnly ${reasons.osOnly.length}` +
` + requires ${reasons.requires.length}`,
); );
console.log('Сверить с "Skipped: N" из прогона runner.mjs с теми же --runtime.'); console.log('Сверить с "Skipped: N" из прогона runner.mjs с теми же --runtime.');
+8 -1
View File
@@ -282,7 +282,7 @@ function cleanupWorkspace(ws) {
// выбор платформы пришлось бы писать двумя копиями под osOnly. // выбор платформы пришлось бы писать двумя копиями под osOnly.
function substPlaceholders(s, workDir) { function substPlaceholders(s, workDir) {
const fakePath = join(workDir, process.platform === 'win32' ? 'fake.cmd' : 'fake.sh'); const fakePath = join(workDir, process.platform === 'win32' ? 'fake.cmd' : 'fake.sh');
return s.replaceAll('{workDir}', workDir).replaceAll('{fakePlatform}', fakePath); return s.replaceAll('{workDir}', workDir).replaceAll('{fakePlatform}', fakePath).replaceAll('{repoRoot}', REPO_ROOT);
} }
function substDeep(value, workDir) { function substDeep(value, workDir) {
@@ -835,6 +835,13 @@ async function runCaseAsync(testCase, opts) {
return { id: testCase.id, skill: testCase.skillDir, name: testCase.name, passed: true, skipped: true, errors: [], elapsed: '0.0s' }; return { id: testCase.id, skill: testCase.skillDir, name: testCase.name, passed: true, skipped: true, errors: [], elapsed: '0.0s' };
} }
// requires: кейс опирается на локальные данные, которые в репозиторий не входят (XSD-схемы 1С
// в .v8-xsd — их правообладатель 1С). Пути от корня репозитория; нет хотя бы одного — skipped.
// Не external: тот отдаёт каталог на месте только для чтения, а кейсу нужно разложить свои файлы.
if (caseData.requires && ![].concat(caseData.requires).every(p => existsSync(resolve(REPO_ROOT, p)))) {
return { id: testCase.id, skill: testCase.skillDir, name: testCase.name, passed: true, skipped: true, errors: [], elapsed: '0.0s' };
}
try { try {
const skillCasesDir = join(CASES, testCase.skillDir); const skillCasesDir = join(CASES, testCase.skillDir);
const fixturePath = ensureSetup(setupName, opts.runtime, skillCasesDir); const fixturePath = ensureSetup(setupName, opts.runtime, skillCasesDir);