Report errors also to Pipeline message log. #339
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Verification Pipeline for Parameters | |
on: | |
push: | |
workflow_dispatch: | |
jobs: | |
Params_Default: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
Params_PythonVersions: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
python_version_list: "3.9 3.10 pypy-3.8 pypy-3.9" | |
Params_Systems: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
system_list: "windows mingw32 mingw64" | |
Params_Include: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
python_version_list: "3.10" | |
system_list: "ubuntu windows macos" | |
include_list: "ubuntu:3.11 ubuntu:3.12" | |
Params_Exclude: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
python_version_list: "3.10" | |
system_list: "ubuntu windows macos" | |
exclude_list: "windows:3.10 windows:3.11" | |
Params_Disable: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
python_version_list: "3.10" | |
system_list: "ubuntu windows macos" | |
disable_list: "windows:3.10 windows:3.11" | |
Params_All: | |
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r1 | |
with: | |
name: Example | |
python_version_list: "3.10 3.11" | |
system_list: "ubuntu windows macos" | |
include_list: "windows:3.8 windows:3.9 windows:3.12" | |
exclude_list: "macos:3.10 macos:3.11" | |
Params_Check: | |
needs: | |
- Params_Default | |
- Params_PythonVersions | |
- Params_Systems | |
- Params_Include | |
- Params_Exclude | |
- Params_Disable | |
- Params_All | |
runs-on: ubuntu-24.04 | |
defaults: | |
run: | |
shell: python | |
steps: | |
- name: Install dependencies | |
shell: bash | |
run: pip install --disable-pip-version-check --break-system-packages pyTooling | |
# Params_Default | |
- name: Checking results from 'Params_Default' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.8", "3.9", "3.10", "3.11", "3.12"] | |
expectedSystems = ["ubuntu", "windows", "macos"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"] | |
expectedJobs.remove("macos:3.8") | |
expectedJobs.remove("macos:3.9") | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_Default.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_Default.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_Default.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_PythonVersions | |
- name: Checking results from 'Params_PythonVersions' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.9", "3.10", "pypy-3.8", "pypy-3.9"] | |
expectedSystems = ["ubuntu", "windows", "macos"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"] | |
expectedJobs.remove("macos:3.9") | |
expectedJobs.remove("macos:pypy-3.8") | |
expectedJobs.remove("macos:pypy-3.9") | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_PythonVersions.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_PythonVersions.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_PythonVersions.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_Systems | |
- name: Checking results from 'Params_Systems' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.8", "3.9", "3.10", "3.11", "3.12"] | |
expectedSystems = ["windows"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw32:3.11", "mingw64:3.11"] | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_Systems.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_Systems.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_Systems.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_Include | |
- name: Checking results from 'Params_Include' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.10"] | |
expectedSystems = ["ubuntu", "windows", "macos"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["ubuntu:3.11", "ubuntu:3.12"] | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_Include.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_Include.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_Include.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_Exclude | |
- name: Checking results from 'Params_Exclude' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.10"] | |
expectedSystems = ["ubuntu", "macos"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_Exclude.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_Exclude.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_Disable | |
- name: Checking results from 'Params_Disable' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.10"] | |
expectedSystems = ["ubuntu", "macos"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_Exclude.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_Exclude.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) | |
# Params_All | |
- name: Checking results from 'Params_All' | |
run: | | |
from json import loads as json_loads | |
from sys import exit | |
from pyTooling.Common import zipdicts | |
expectedPythonVersion = "3.12" | |
expectedPythons = ["3.10", "3.11"] | |
expectedSystems = ["ubuntu", "windows"] | |
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["windows:3.8", "windows:3.9", "windows:3.12"] | |
expectedName = "Example" | |
expectedArtifacts = { | |
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", | |
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", | |
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", | |
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", | |
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", | |
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", | |
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML", | |
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON", | |
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML", | |
"statictyping_html": f"{expectedName}-StaticTyping-HTML", | |
"package_all": f"{expectedName}-Packages", | |
"documentation_html": f"{expectedName}-Documentation-HTML", | |
"documentation_latex": f"{expectedName}-Documentation-LaTeX", | |
"documentation_pdf": f"{expectedName}-Documentation-PDF", | |
} | |
actualPythonVersion = """${{ needs.Params_All.outputs.python_version }}""" | |
actualPythonJobs = json_loads("""${{ needs.Params_All.outputs.python_jobs }}""".replace("'", '"')) | |
actualArtifactNames = json_loads("""${{ needs.Params_All.outputs.artifact_names }}""".replace("'", '"')) | |
errors = 0 | |
if actualPythonVersion != expectedPythonVersion: | |
print(f"'python_version' does not match: '{actualPythonVersion}' != '{expectedPythonVersion}'.") | |
errors += 1 | |
if len(actualPythonJobs) != len(expectedJobs): | |
print(f"Number of 'python_jobs' does not match: {len(actualPythonJobs)} != {len(expectedJobs)}.") | |
print("Actual jobs:") | |
for job in actualPythonJobs: | |
print(f" {job['system']}:{job['python']}") | |
print("Expected jobs:") | |
for job in expectedJobs: | |
print(f" {job}") | |
errors += 1 | |
if len(actualArtifactNames) != len(expectedArtifacts): | |
print(f"Number of 'artifact_names' does not match: {len(actualArtifactNames)} != {len(expectedArtifacts)}.") | |
errors += 1 | |
else: | |
for key, actual, expected in zipdicts(actualArtifactNames, expectedArtifacts): | |
if actual != expected: | |
print(f"Artifact name '{key}' does not match: {actual} != {expected}.") | |
errors += 1 | |
if errors == 0: | |
print(f"All checks PASSED.") | |
exit(errors) |