Class GuardCouplingReader
π A different signal from RequirePredicateReader, and the distinction is the whole
point. That reader takes a guard's literal and answers which VALUE matters.
This one ignores values entirely and answers which TUPLE matters. Same source, orthogonal
signal, and only the second says anything about combination.
Why co-occurrence is worth reading at all. A coupling is often neither declared by any type nor derivable by reasoning about the domain. Production code that checks a widget's brittleness and its colour together looks arbitrary until colour turns out to drive sunlight exposure. Worse, some couplings have no mechanism at all β an insurer prices a fitted tow bar higher not because the vehicle tows, but because that population claims more often. No amount of domain understanding produces that. It exists in exactly one place: the condition somebody wrote. The code is the domain expert.
π Where this sits in the combinatorial-testing model. Nie & Leung's CT survey (ACM
Computing Surveys 43(2), 2011) formalises the model as Model_SUT(P, V, R, C) β parameters,
value sets, the interaction relations R, and the constraints C. This class
computes R; C comes from constrained types and require.
π R is the COVERING REQUIREMENT SET, not merely an observation β Def. 2.2.3:
"Let R denote the set of the interaction relation set. R is the covering
requirements for the SUT β¦ If CA covers all the combinations required by R, we call CA a
covering array." When R's elements have differing sizes the result is a variable
strength covering array, which is precisely what a coupling of arity 3 asks for. So the
couplings this returns are requirements the array must satisfy, not hints it may use.
π Claim nothing novel for the idea. That same survey names the automated route β
"static analysis, such as program slicing, can also be used to identify the interactions between
parameters" β citing Schroeder & Korel (2000) on inputβoutput analysis and Cheng, Dumitrescu
& Schroeder (2003). Deriving R rather than eliciting it from documents and interviews
is established prior art. What differs here is the signal and its cost: theirs is dataflow-based
inputβoutput analysis, ours is syntactic co-occurrence in a control condition, read off the
parse tree with no execution β and our C is enforced by the compiler rather than modelled.
π Driven by a parse CONTEXT, never by name β as with RequirePredicateReader, the
caller supplies the construct's own subtree, so functions, methods, constructors, operators and
dynamic functions are all covered by one traversal.
β οΈ ADDITIVE ONLY, and that is what makes it blindness-safe. A coupling
guarantees combinations the generator would otherwise leave to a covering array's
discretion (SPEC-test-data-generation.md Β§0's A_use ADDITIVE row). It must never
prune: "nothing couples these two today" is precisely the implementation-under-test
assumption use-directed reduction is warned against, because the omission being hunted looks
identical to an absent coupling.
β οΈ The relation is NOT transitively closed, deliberately. If aβb couple
and bβc couple, (a,c) is not a coupling. Taking the closure makes
everything couple everything, the array degenerates to exhaustive, and the signal disappears
exactly where the code is richest. Only literal observed sets are reported.
β οΈ Anchors are PERISHABLE, hence GuardCouplingReader.GuardCoupling.line and GuardCouplingReader.GuardCoupling.guardText. A declared constraint changes only when someone edits the
declaration, and the compiler revalidates every generated test on the next build. A guard is not
like that: it moves, narrows or disappears when the world changes β a paint defect is fixed, an
actuarial model re-fitted β and nothing notices. Recording where a coupling came from is what lets
a later staleness check ask whether it is still there.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordParameters, or fields of one parameter, that a condition (and its enclosing conditions) test together, with the provenance needed to re-check it later. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionread(org.antlr.v4.runtime.tree.ParseTree constructCtx, List<ISymbol> callParameters, ParsedModule module) Read every coupling expressed by a control condition inconstructCtx.
-
Constructor Details
-
GuardCouplingReader
public GuardCouplingReader()
-
-
Method Details
-
read
public List<GuardCouplingReader.GuardCoupling> read(org.antlr.v4.runtime.tree.ParseTree constructCtx, List<ISymbol> callParameters, ParsedModule module) Read every coupling expressed by a control condition inconstructCtx.Conditions are taken from the grammar's labelled
control=expressionβ coveringif/when(viapreFlowAndControl),switch/given(which uses the same rule) and bothwhileforms.π Nesting counts, and reading only single conditions was a real gap.
if a > 3containingif b < 7is the same discrimination asif a > 3 and b < 7β the block is reached only when both hold β so each condition is read together with its ENCLOSING chain. Branch polarity is irrelevant here: reaching an inner condition inside anelsestill required the outer one to be evaluated, so the coupling holds either way. That is why this needs none of the dominator careSymbolicEnginetakes, where the polarity decides soundness.- Parameters:
constructCtx- the construct's parse subtreecallParameters- the construct's parameters, in declaration order, for index mappingmodule- the parsed module, to resolve identifier references to symbols- Returns:
- the couplings found, de-duplicated, in first-appearance order; never null
-