Skip to content

Commit edfbfc7

Browse files
committed
Modify the generatesSources() test clause in compile_testing's JavaSourcesSubject so that it relies on fully-qualified names instead of source paths for finding diffbase candidates
------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=65398493
1 parent 4203f33 commit edfbfc7

File tree

4 files changed

+326
-33
lines changed

4 files changed

+326
-33
lines changed

src/main/java/com/google/testing/compile/JavaSourcesSubject.java

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
import com.google.common.base.Predicate;
2424
import com.google.common.collect.FluentIterable;
2525
import com.google.common.collect.ImmutableList;
26+
import com.google.common.collect.ImmutableMap;
2627
import com.google.common.collect.ImmutableSet;
2728
import com.google.common.collect.Iterables;
2829
import com.google.common.collect.Lists;
30+
import com.google.common.collect.Maps;
2931
import com.google.common.io.ByteStreams;
3032
import com.google.testing.compile.Compilation.Result;
3133

@@ -36,6 +38,7 @@
3638

3739
import java.io.IOException;
3840
import java.util.Arrays;
41+
import java.util.Map;
3942

4043
import javax.annotation.processing.Processor;
4144
import javax.tools.Diagnostic;
@@ -254,38 +257,73 @@ public GeneratedPredicateClause and() {
254257
@Override
255258
public SuccessfulCompilationClause generatesSources(JavaFileObject first,
256259
JavaFileObject... rest) {
257-
ImmutableList<JavaFileObject> generatedSources = result.generatedSources();
258-
Iterable<? extends CompilationUnitTree> actualCompilationUnits =
259-
Compilation.parse(generatedSources);
260+
261+
final FluentIterable<? extends CompilationUnitTree> actualTrees = FluentIterable.from(
262+
Compilation.parse(result.generatedSources()));
263+
final FluentIterable<? extends CompilationUnitTree> expectedTrees = FluentIterable.from(
264+
Compilation.parse(Lists.asList(first, rest)));
260265
final EqualityScanner scanner = new EqualityScanner();
261-
for (final CompilationUnitTree expectedTree : Compilation.parse(Lists.asList(first, rest))) {
262-
Optional<? extends CompilationUnitTree> found =
263-
Iterables.tryFind(actualCompilationUnits, new Predicate<CompilationUnitTree>() {
264-
@Override
265-
public boolean apply(CompilationUnitTree actualTree) {
266-
return scanner.visitCompilationUnit(expectedTree, actualTree);
267-
}
268-
});
269-
if (!found.isPresent()) {
270-
final JavaFileObject expected = expectedTree.getSourceFile();
271-
Optional<JavaFileObject> actual =
272-
FluentIterable.from(generatedSources).firstMatch(new Predicate<JavaFileObject>() {
273-
@Override public boolean apply(JavaFileObject generatedFile) {
274-
return generatedFile.toUri().getPath().endsWith(expected.toUri().getPath());
266+
267+
Function<? super CompilationUnitTree, ImmutableSet<String>> getTypesFunction =
268+
new Function<CompilationUnitTree, ImmutableSet<String>>() {
269+
@Override public ImmutableSet<String> apply(CompilationUnitTree compilationUnit) {
270+
return TypeScanner.getTopLevelTypes(compilationUnit);
271+
}
272+
};
273+
274+
final ImmutableMap<? extends CompilationUnitTree, ImmutableSet<String>> expectedTreeTypes =
275+
Maps.toMap(expectedTrees, getTypesFunction);
276+
final ImmutableMap<? extends CompilationUnitTree, ImmutableSet<String>> actualTreeTypes =
277+
Maps.toMap(actualTrees, getTypesFunction);
278+
final ImmutableMap<? extends CompilationUnitTree, Optional<? extends CompilationUnitTree>>
279+
matchedTrees = Maps.toMap(expectedTrees,
280+
new Function<CompilationUnitTree, Optional<? extends CompilationUnitTree>>() {
281+
@Override public Optional<? extends CompilationUnitTree> apply(
282+
final CompilationUnitTree expectedTree) {
283+
return Iterables.tryFind(actualTrees,
284+
new Predicate<CompilationUnitTree>() {
285+
@Override public boolean apply(CompilationUnitTree actualTree) {
286+
return expectedTreeTypes.get(expectedTree).equals(
287+
actualTreeTypes.get(actualTree));
288+
}
289+
});
275290
}
276291
});
277-
if (actual.isPresent()) {
278-
CharSequence actualSource = null;
279-
try {
280-
actualSource = actual.get().getCharContent(false);
281-
} catch (IOException e) {
282-
throw new RuntimeException("Exception reading source content.", e);
283-
}
284-
failureStrategy.fail("Generated file " + expected.getName()
285-
+ " did not match expectation. Found:\n"
286-
+ (actualSource == null ? "no source found" : actualSource));
287-
} else {
288-
failureStrategy.fail("Did not find a source file named " + expected.getName());
292+
293+
for (Map.Entry<? extends CompilationUnitTree, Optional<? extends CompilationUnitTree>>
294+
matchedTreePair : matchedTrees.entrySet()) {
295+
final CompilationUnitTree expectedTree = matchedTreePair.getKey();
296+
JavaFileObject expectedSource = expectedTree.getSourceFile();
297+
if (!matchedTreePair.getValue().isPresent()) {
298+
failureStrategy.fail(String.format("None of the sources generated declared the same "
299+
+ "top-level types as the expected source at %s (Types: %s).\n "
300+
+ "Generated files at:\n%s",
301+
expectedSource.getName(), expectedTreeTypes.get(expectedTree),
302+
Joiner.on('\n').join(
303+
actualTrees.transform(new Function<CompilationUnitTree, String>() {
304+
@Override public String apply(CompilationUnitTree generated) {
305+
return String.format("%s (Types: %s)",
306+
generated.getSourceFile().getName(),
307+
actualTreeTypes.get(generated));
308+
}
309+
})
310+
.toList())));
311+
}
312+
313+
CompilationUnitTree actualTree = matchedTreePair.getValue().get();
314+
JavaFileObject actualSource = actualTree.getSourceFile();
315+
316+
if (!scanner.visitCompilationUnit(expectedTree, actualTree)) {
317+
try {
318+
String expectedContent = expectedSource.getCharContent(false).toString();
319+
String actualContent = actualSource.getCharContent(false).toString();
320+
failureStrategy.fail(String.format("The source generated at %s declared the same "
321+
+ "top-level types as the expected source at %s, but didn't match exactly. "
322+
+ "Actual:\n\n%s\n\nExpected:\n\n%s", actualSource.getName(),
323+
expectedSource.getName(), actualContent, expectedContent));
324+
} catch (IOException e) {
325+
throw new IllegalStateException("Couldn't read from JavaFileObject when it was already "
326+
+ "in memory.", e);
289327
}
290328
}
291329
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (C) 2014 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.testing.compile;
17+
18+
import static com.sun.source.tree.Tree.Kind.CLASS;
19+
import static com.sun.source.tree.Tree.Kind.COMPILATION_UNIT;
20+
import static com.sun.source.tree.Tree.Kind.EXPRESSION_STATEMENT;
21+
import static com.sun.source.tree.Tree.Kind.IDENTIFIER;
22+
import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT;
23+
24+
import com.google.common.base.Function;
25+
import com.google.common.collect.FluentIterable;
26+
import com.google.common.collect.ImmutableSet;
27+
import com.google.common.collect.Sets;
28+
29+
import com.sun.source.tree.ClassTree;
30+
import com.sun.source.tree.CompilationUnitTree;
31+
import com.sun.source.tree.ExpressionStatementTree;
32+
import com.sun.source.tree.IdentifierTree;
33+
import com.sun.source.tree.MemberSelectTree;
34+
import com.sun.source.tree.Tree;
35+
import com.sun.source.util.TreeScanner;
36+
37+
import java.util.Set;
38+
39+
/**
40+
* Provides information about the set of types that are declared by a
41+
* {@code CompilationUnitTree}.
42+
*/
43+
final class TypeScanner {
44+
private static final NameVisitor nameVisitor = new NameVisitor();
45+
private TypeScanner() {}
46+
47+
/**
48+
* Returns a set of strings containing the fully qualified names of all
49+
* the types that are declared by the given CompilationUnitTree
50+
*/
51+
static ImmutableSet<String> getTopLevelTypes(CompilationUnitTree t) {
52+
return ImmutableSet.copyOf(nameVisitor.scan(t, null));
53+
}
54+
55+
/**
56+
* A {@link SimpleTreeVisitor} for determining type declarations
57+
*/
58+
@SuppressWarnings("restriction") // Sun APIs usage intended
59+
static final class NameVisitor extends TreeScanner<Set<String>, Void> {
60+
61+
private static final Set<Tree.Kind> RELEVANT_KINDS = Sets.immutableEnumSet(
62+
CLASS, COMPILATION_UNIT, EXPRESSION_STATEMENT, IDENTIFIER, MEMBER_SELECT);
63+
64+
@Override
65+
public Set<String> scan(Tree node, Void v) {
66+
return (node != null) && RELEVANT_KINDS.contains(node.getKind()) ?
67+
node.accept(this, v) : ImmutableSet.<String>of();
68+
}
69+
70+
@Override
71+
public Set<String> reduce(Set<String> r1, Set<String> r2) {
72+
return Sets.union(r1, r2);
73+
}
74+
75+
@Override
76+
public Set<String> visitClass(ClassTree reference, Void v) {
77+
return ImmutableSet.of(reference.getSimpleName().toString());
78+
}
79+
80+
@Override
81+
public Set<String> visitExpressionStatement(ExpressionStatementTree reference, Void v) {
82+
return scan(reference.getExpression(), v);
83+
}
84+
85+
@Override
86+
public Set<String> visitIdentifier(IdentifierTree reference, Void v) {
87+
return ImmutableSet.of(reference.getName().toString());
88+
}
89+
90+
@Override
91+
public Set<String> visitMemberSelect(MemberSelectTree reference, Void v) {
92+
Set<String> expressionSet = scan(reference.getExpression(), v);
93+
if (expressionSet.size() != 1) {
94+
throw new AssertionError("Internal error in NameFinder. Expected to find exactly one "
95+
+ "identifier in the expression set. Found " + expressionSet);
96+
}
97+
String expressionStr = expressionSet.iterator().next();
98+
return ImmutableSet.of(String.format("%s.%s", expressionStr, reference.getIdentifier()));
99+
}
100+
101+
@Override
102+
public Set<String> visitCompilationUnit(CompilationUnitTree reference, Void v) {
103+
Set<String> packageSet = reference.getPackageName() == null ?
104+
ImmutableSet.of("") : scan(reference.getPackageName(), v);
105+
if (packageSet.size() != 1) {
106+
throw new AssertionError("Internal error in NameFinder. Expected to find at most one " +
107+
"package identifier. Found " + packageSet);
108+
}
109+
final String packageName = packageSet.isEmpty() ? "" : packageSet.iterator().next();
110+
Set<String> typeDeclSet = scan(reference.getTypeDecls(), v);
111+
return FluentIterable.from(typeDeclSet)
112+
.transform(new Function<String, String>() {
113+
@Override public String apply(String typeName) {
114+
return packageName.isEmpty() ? typeName :
115+
String.format("%s.%s", packageName, typeName);
116+
}
117+
}).toSet();
118+
}
119+
}
120+
}

src/test/java/com/google/testing/compile/JavaSourcesSubjectFactoryTest.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public void failsToCompile() {
215215
}
216216

217217
@Test
218-
public void generates() {
218+
public void generatesSources() {
219219
ASSERT.about(javaSource())
220220
.that(JavaFileObjects.forResource("HelloWorld.java"))
221221
.processedWith(new GeneratingProcessor())
@@ -226,7 +226,7 @@ public void generates() {
226226
}
227227

228228
@Test
229-
public void generatesUnexpectedSource() {
229+
public void generatesSources_failOnUnexpected() {
230230
String failingExpectationSource = "abstract class Blah {}";
231231
try {
232232
VERIFY.about(javaSource())
@@ -236,13 +236,33 @@ public void generatesUnexpectedSource() {
236236
.and().generatesSources(JavaFileObjects.forSourceString(
237237
GeneratingProcessor.GENERATED_CLASS_NAME,
238238
failingExpectationSource));
239+
fail();
239240
} catch (VerificationException expected) {
240-
ASSERT.that(expected.getMessage()).contains(
241-
"Generated file Blah.java did not match expectation. Found:");
241+
ASSERT.that(expected.getMessage()).contains("didn't match exactly");
242+
ASSERT.that(expected.getMessage()).contains(GeneratingProcessor.GENERATED_CLASS_NAME);
242243
ASSERT.that(expected.getMessage()).contains(GeneratingProcessor.GENERATED_SOURCE);
243244
}
244245
}
245246

247+
@Test
248+
public void generatesSources_failWithNoCandidates() {
249+
String failingExpectationName = "ThisIsNotTheRightFile";
250+
String failingExpectationSource = "abstract class ThisIsNotTheRightFile {}";
251+
try {
252+
VERIFY.about(javaSource())
253+
.that(JavaFileObjects.forResource("HelloWorld.java"))
254+
.processedWith(new GeneratingProcessor())
255+
.compilesWithoutError()
256+
.and().generatesSources(JavaFileObjects.forSourceString(
257+
failingExpectationName,
258+
failingExpectationSource));
259+
} catch (VerificationException expected) {
260+
ASSERT.that(expected.getMessage()).contains("None of the sources generated");
261+
ASSERT.that(expected.getMessage()).contains(GeneratingProcessor.GENERATED_CLASS_NAME);
262+
ASSERT.that(expected.getMessage()).contains(failingExpectationName);
263+
}
264+
}
265+
246266
@Test
247267
public void invokesMultipleProcesors() {
248268
NoOpProcessor noopProcessor1 = new NoOpProcessor();

0 commit comments

Comments
 (0)