1919import static com .google .common .truth .Truth .assertAbout ;
2020import static com .google .testing .compile .JavaFileObjects .asByteSource ;
2121import static com .google .testing .compile .TreeDiffer .diffCompilationUnits ;
22+ import static com .google .testing .compile .TreeDiffer .matchCompilationUnits ;
2223import static java .nio .charset .StandardCharsets .UTF_8 ;
2324
2425import com .google .common .base .Joiner ;
3132import com .sun .source .tree .CompilationUnitTree ;
3233import java .io .IOException ;
3334import java .nio .charset .Charset ;
35+ import java .util .function .BiFunction ;
3436import javax .annotation .Nullable ;
3537import javax .tools .JavaFileObject ;
3638
@@ -113,17 +115,65 @@ public StringSubject contentsAsUtf8String() {
113115 }
114116
115117 /**
116- * Asserts that the actual file is a source file with contents equivalent to {@code
118+ * Asserts that the actual file is a source file that has an equivalent <a
119+ * href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">AST</a> to that of {@code
117120 * expectedSource}.
118121 */
119122 public void hasSourceEquivalentTo (JavaFileObject expectedSource ) {
123+ performTreeDifference (
124+ expectedSource ,
125+ "is equivalent to" ,
126+ "Expected Source" ,
127+ (expectedResult , actualResult ) ->
128+ diffCompilationUnits (
129+ getOnlyElement (expectedResult .compilationUnits ()),
130+ getOnlyElement (actualResult .compilationUnits ())));
131+ }
132+
133+ /**
134+ * Asserts that the every node in the <a
135+ * href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">AST</a> of {@code expectedPattern}
136+ * exists in the actual file's AST, in the same order.
137+ *
138+ * <p>Methods, constructors, fields, and types that are in the pattern must have the exact same
139+ * modifiers and annotations as the actual AST. Ordering of AST nodes is also important (i.e. a
140+ * type with identical members in a different order will fail the assertion). Types must match the
141+ * entire type declaration: type parameters, {@code extends}/{@code implements} clauses, etc.
142+ * Methods must also match the throws clause as well.
143+ *
144+ * <p>The body of a method or constructor, or field initializer in the actual AST must match the
145+ * pattern in entirety if the member is present in the pattern.
146+ *
147+ * <p>Said in another way (from a graph-theoretic perspective): the pattern AST must be a subgraph
148+ * of the actual AST. If a method, constructor, or field is in the pattern, that entire subtree,
149+ * including modifiers and annotations, must be equal to the corresponding subtree in the actual
150+ * AST (no proper subgraphs).
151+ */
152+ public void containsElementsIn (JavaFileObject expectedPattern ) {
153+ performTreeDifference (
154+ expectedPattern ,
155+ "contains elements in" ,
156+ "Expected Pattern" ,
157+ (expectedResult , actualResult ) ->
158+ matchCompilationUnits (
159+ getOnlyElement (expectedResult .compilationUnits ()),
160+ actualResult .trees (),
161+ getOnlyElement (actualResult .compilationUnits ()),
162+ expectedResult .trees ()));
163+ }
164+
165+ private void performTreeDifference (
166+ JavaFileObject expected ,
167+ String failureVerb ,
168+ String expectedTitle ,
169+ BiFunction <ParseResult , ParseResult , TreeDifference > differencingFunction ) {
120170 ParseResult actualResult = Parser .parse (ImmutableList .of (actual ()));
121171 CompilationUnitTree actualTree = getOnlyElement (actualResult .compilationUnits ());
122172
123- ParseResult expectedResult = Parser .parse (ImmutableList .of (expectedSource ));
173+ ParseResult expectedResult = Parser .parse (ImmutableList .of (expected ));
124174 CompilationUnitTree expectedTree = getOnlyElement (expectedResult .compilationUnits ());
125175
126- TreeDifference treeDifference = diffCompilationUnits ( expectedTree , actualTree );
176+ TreeDifference treeDifference = differencingFunction . apply ( expectedResult , actualResult );
127177
128178 if (!treeDifference .isEmpty ()) {
129179 String diffReport =
@@ -134,17 +184,17 @@ public void hasSourceEquivalentTo(JavaFileObject expectedSource) {
134184 fail (
135185 Joiner .on ('\n' )
136186 .join (
137- String .format ("is equivalent to <%s>." , expectedSource .toUri ().getPath ()),
187+ String .format ("%s <%s>." , failureVerb , expected .toUri ().getPath ()),
138188 "" ,
139189 "Diffs:" ,
140190 "======" ,
141191 "" ,
142192 diffReport ,
143193 "" ,
144- "Expected Source :" ,
194+ expectedTitle + " :" ,
145195 "================" ,
146196 "" ,
147- expectedSource .getCharContent (false ),
197+ expected .getCharContent (false ),
148198 "" ,
149199 "Actual Source:" ,
150200 "==============" ,
0 commit comments