From 7e4f99b64588df386626b4cdad60b5f519b13ade Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Sat, 29 Aug 2026 22:36:12 -0700 Subject: [PATCH 1/3] test(coverage): raise core function coverage from 71.6% to 83.2% MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nine new spec bundles close the biggest measured gaps in the wheelstest system surface, the per-database adapter SQL generators, the model ScopeChain/QueryBuilder chain, and global/migrator helpers. - wheelstest/system: BaseSpec DSL aliases (story/feature/given/scenario/ when + focused/x variants), assertion facade (assert/fail/ expectedException/addAssertions), expectAll unrolling, querySim, engine predicates, getProperty; the Assertion matcher library (closeTo/between/instance/null/type/case-sensitive string/struct/length matchers + query/XML equality); Expectation matchers (toBeBetween/ toSatisfy/toBeCloseTo/case matchers/deep keys/membership/negation); Util/Env/XMLConverter/MixerUtil/BaseReporter utilities; MockBox createEmptyMock + the injected call-count verification surface; and a run()-less xUnit bundle that exercises the legacy UnitRunner path (setup/teardown, expectedException contract). - databaseAdapters: pure SQL-generation units for MySQL/H2/PostgreSQL/ SQL Server/Oracle models ($upsertSQL, quoting, defaults, locking clauses, random order) and migrators (keys, indexes, renames, drops, DDL fragments) — no live per-DB connection required, so every matrix leg exercises them. - model: ScopeChain terminal finders/aggregates/updateAll/deleteAll/ findEach/findInBatches, QueryBuilder.offset, the remaining lifecycle callback registrations, ignoredColumns/sharedModel/isClass/isInstance/ errorsOnBase/withAdvisoryLock. - global/migrator: TableDefinition/ViewDefinition builders, Migration helpers, $convertToStringBoolean/BoxLangSlashDatetime/$normalizePath/ $boxLangVersionMessage/pluginNames/injector. Coverage tooling also tightened: the tag-based cffunction matcher now captures names from the unmasked source (masked attribute values used to capture the NEXT attribute's name), whole-file template pseudo-entries and migrator generator templates are excluded from the denominator, and the measured baseline is updated in tools/code-quality/README.md. Measured (Lucee 7 + SQLite): 71.6% -> 83.2% function coverage (1,949 -> 2,175 covered). Full suite: 5,513 pass / 0 fail / 0 error. Signed-off-by: Peter Amiri --- tools/code-quality/README.md | 5 +- tools/code-quality/cfml-coverage.py | 35 +- .../database/AdapterSqlGenerationSpec.cfc | 413 ++++++++++++++++++ .../global/MigratorGlobalCoverageSpec.cfc | 133 ++++++ .../specs/model/ModelSurfaceCoverageSpec.cfc | 146 +++++++ .../wheelstest/AssertionMatchersSpec.cfc | 178 ++++++++ .../specs/wheelstest/BaseSpecDslAliasSpec.cfc | 220 ++++++++++ .../wheelstest/ExpectationMatchersSpec.cfc | 129 ++++++ .../tests/specs/wheelstest/MockBoxSpec.cfc | 66 +++ .../tests/specs/wheelstest/SystemUtilSpec.cfc | 185 ++++++++ .../specs/wheelstest/XUnitStyleLegacyTest.cfc | 47 ++ 11 files changed, 1552 insertions(+), 5 deletions(-) create mode 100644 vendor/wheels/tests/specs/database/AdapterSqlGenerationSpec.cfc create mode 100644 vendor/wheels/tests/specs/global/MigratorGlobalCoverageSpec.cfc create mode 100644 vendor/wheels/tests/specs/model/ModelSurfaceCoverageSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/AssertionMatchersSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/BaseSpecDslAliasSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/ExpectationMatchersSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/MockBoxSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/SystemUtilSpec.cfc create mode 100644 vendor/wheels/tests/specs/wheelstest/XUnitStyleLegacyTest.cfc diff --git a/tools/code-quality/README.md b/tools/code-quality/README.md index 42d40fd30..177b21397 100644 --- a/tools/code-quality/README.md +++ b/tools/code-quality/README.md @@ -63,8 +63,9 @@ python3 tools/code-quality/cfml-coverage.py combine vendor/wheels \ python3 tools/code-quality/cfml-coverage.py revert vendor/wheels ``` -Baseline (Lucee 7 + SQLite, measured 2026-08): **~71% function coverage** -(1,937/2,723 functions). Known caveats: +Baseline (Lucee 7 + SQLite, measured 2026-08): **83.2% function coverage** +(2,175/2,614 functions — template pseudo-entries and migrator generator +templates excluded from the denominator). Known caveats: - Coverage is per-leg: `databaseAdapters/*` paths for MySQL/Oracle/SQLServer/ CockroachDB only execute on those matrix legs — a SQLite-only measurement diff --git a/tools/code-quality/cfml-coverage.py b/tools/code-quality/cfml-coverage.py index 54609aa29..cd8ab4aeb 100644 --- a/tools/code-quality/cfml-coverage.py +++ b/tools/code-quality/cfml-coverage.py @@ -187,6 +187,12 @@ def _script_insertion_points(masked): def _files(root): for dirpath, dirnames, filenames in os.walk(root): dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS] + # migrator/templates/* are generator payloads — the scaffolding copied + # into a user's app/db/migrate on `wheels migrate`. The originals never + # execute, so instrumenting them only adds permanently-uncovered noise. + if os.path.relpath(dirpath, root).replace(os.sep, '/').endswith('migrator/templates'): + dirnames[:] = [] + continue for fn in filenames: if fn.lower().endswith(('.cfc', '.cfm')): yield os.path.join(dirpath, fn) @@ -207,8 +213,14 @@ def instrument(root): # first, then apply them right-to-left so earlier insertions never # shift later positions. matches = [(pos, 'script', name) for pos, name in _script_insertion_points(masked)] - for m in TAG_FN.finditer(masked): - brace = masked.find('>', m.end()) + # Tag-based cffunctions: match against the ORIGINAL text — masked + # attribute values (blanked quotes) make `name\s*=\s*["']?...` skip + # the value and capture the NEXT attribute's name. Filter out matches + # that start inside a masked (comment/string) region. + for m in TAG_FN.finditer(text): + if m.start() < len(masked) and masked[m.start()] == ' ': + continue + brace = text.find('>', m.end()) if brace >= 0: matches.append((brace + 1, 'tag', m.group(1))) matches.sort(key=lambda t: -t[0]) @@ -259,7 +271,23 @@ def combine(root, coverage_path, complexity_path): complexity = {r['id']: int(r['complexity']) for r in raw_complexity} # cov keys are ":"; complexity ids are "::" rows = [] + skipped_templates = 0 for cid, comp in complexity.items(): + rel = cid.split(':')[0] + # Whole-file pseudo-entries (`