1 /*
2  * Copyright (C) 2019 The Android Open Source Project
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 
17 package android.processor.compat.unsupportedappusage;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.testing.compile.Compilation;
22 import com.google.testing.compile.CompilationSubject;
23 import com.google.testing.compile.Compiler;
24 import com.google.testing.compile.JavaFileObjects;
25 
26 import org.junit.Test;
27 
28 import java.io.IOException;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.stream.Collectors;
33 
34 import javax.tools.JavaFileObject;
35 import javax.tools.StandardLocation;
36 
37 public class UnsupportedAppUsageProcessorTest {
38 
39     private static final JavaFileObject ANNOTATION = JavaFileObjects.forSourceLines(
40             "android.compat.annotation.UnsupportedAppUsage",
41             "package android.compat.annotation;",
42             "public @interface UnsupportedAppUsage {",
43             "    String expectedSignature() default \"\";\n",
44             "    String someProperty() default \"\";",
45             "    String overrideSourcePosition() default \"\";",
46             "    String implicitMember() default \"\";",
47             "}");
48 
compile(JavaFileObject source)49     private Compilation compile(JavaFileObject source) {
50         Compilation compilation =
51                 Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
52                         .compile(ANNOTATION, source);
53         CompilationSubject.assertThat(compilation).succeeded();
54         return compilation;
55     }
56 
compileAndReadCsv(JavaFileObject source, String filename)57     private CsvReader compileAndReadCsv(JavaFileObject source, String filename) throws IOException {
58         Compilation compilation = compile(source);
59         Optional<JavaFileObject> csv = compilation.generatedFile(
60                 StandardLocation.CLASS_OUTPUT, filename);
61         assertThat(csv.isPresent()).isTrue();
62 
63         return new CsvReader(csv.get().openInputStream());
64     }
65 
66     @Test
testSignatureFormat()67     public void testSignatureFormat() throws Exception {
68         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
69                 "package a.b;",
70                 "import android.compat.annotation.UnsupportedAppUsage;",
71                 "public class Class {",
72                 "  @UnsupportedAppUsage",
73                 "  public void method() {}",
74                 "}");
75         assertThat(compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0)).containsEntry(
76                 "signature", "La/b/Class;->method()V"
77         );
78     }
79 
80     @Test
testSourcePosition()81     public void testSourcePosition() throws Exception {
82         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
83                 "package a.b;", // 1
84                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
85                 "public class Class {", // 3
86                 "  @UnsupportedAppUsage", // 4
87                 "  public void method() {}", // 5
88                 "}");
89         Map<String, String> row = compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0);
90         assertThat(row).containsEntry("startline", "4");
91         assertThat(row).containsEntry("startcol", "3");
92         assertThat(row).containsEntry("endline", "4");
93         assertThat(row).containsEntry("endcol", "23");
94     }
95 
96     @Test
testAnnotationProperties()97     public void testAnnotationProperties() throws Exception {
98         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
99                 "package a.b;", // 1
100                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
101                 "public class Class {", // 3
102                 "  @UnsupportedAppUsage(someProperty=\"value\")", // 4
103                 "  public void method() {}", // 5
104                 "}");
105         assertThat(compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0)).containsEntry(
106                 "properties", "someProperty=%22value%22");
107     }
108 
109     @Test
testSourcePositionOverride()110     public void testSourcePositionOverride() throws Exception {
111         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
112                 "package a.b;", // 1
113                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
114                 "public class Class {", // 3
115                 "  @UnsupportedAppUsage(overrideSourcePosition=\"otherfile.aidl:30:10:31:20\")",
116                 "  public void method() {}", // 5
117                 "}");
118         Map<String, String> row = compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0);
119         assertThat(row).containsEntry("file", "otherfile.aidl");
120         assertThat(row).containsEntry("startline", "30");
121         assertThat(row).containsEntry("startcol", "10");
122         assertThat(row).containsEntry("endline", "31");
123         assertThat(row).containsEntry("endcol", "20");
124         assertThat(row).containsEntry("properties", "");
125     }
126 
127     @Test
testSourcePositionOverrideWrongFormat()128     public void testSourcePositionOverrideWrongFormat() throws Exception {
129         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
130                 "package a.b;", // 1
131                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
132                 "public class Class {", // 3
133                 "  @UnsupportedAppUsage(overrideSourcePosition=\"invalid\")", // 4
134                 "  public void method() {}", // 5
135                 "}");
136         Compilation compilation =
137                 Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
138                         .compile(ANNOTATION, src);
139         CompilationSubject.assertThat(compilation).failed();
140         CompilationSubject.assertThat(compilation).hadErrorContaining(
141                 "Expected overrideSourcePosition to have format "
142                         + "string:int:int:int:int").inFile(src).onLine(4);
143     }
144 
145     @Test
testSourcePositionOverrideInvalidInt()146     public void testSourcePositionOverrideInvalidInt() throws Exception {
147         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
148                 "package a.b;", // 1
149                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
150                 "public class Class {", // 3
151                 "  @UnsupportedAppUsage(overrideSourcePosition=\"otherfile.aidl:a:b:c:d\")", // 4
152                 "  public void method() {}", // 5
153                 "}");
154         Compilation compilation =
155                 Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
156                         .compile(ANNOTATION, src);
157         CompilationSubject.assertThat(compilation).failed();
158         CompilationSubject.assertThat(compilation).hadErrorContaining(
159                 "Expected overrideSourcePosition to have format "
160                         + "string:int:int:int:int").inFile(src).onLine(4);
161     }
162 
163     @Test
testImplicitMemberSkipped()164     public void testImplicitMemberSkipped() throws Exception {
165         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
166                 "package a.b;", // 1
167                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
168                 "public class Class {", // 3
169                 "  @UnsupportedAppUsage(implicitMember=\"foo\")", // 4
170                 "  public void method() {}", // 5
171                 "}");
172         List<JavaFileObject> generatedNonClassFiles =
173                 compile(src)
174                         .generatedFiles()
175                         .stream()
176                         .filter(file -> !file.getName().endsWith(".class"))
177                         .collect(Collectors.toList());
178         assertThat(generatedNonClassFiles).hasSize(0);
179     }
180 
181     @Test
testExpectedSignatureSucceedsIfMatching()182     public void testExpectedSignatureSucceedsIfMatching() throws Exception {
183         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
184                 "package a.b;", // 1
185                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
186                 "public class Class {", // 3
187                 "  @UnsupportedAppUsage(expectedSignature=\"La/b/Class;->method()V\")", // 4
188                 "  public void method() {}", // 5
189                 "}");
190         Compilation compilation =
191                 Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
192                         .compile(ANNOTATION, src);
193         CompilationSubject.assertThat(compilation).succeeded();
194     }
195 
196     @Test
testExpectedSignatureThrowsErrorIfWrong()197     public void testExpectedSignatureThrowsErrorIfWrong() throws Exception {
198         JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
199                 "package a.b;", // 1
200                 "import android.compat.annotation.UnsupportedAppUsage;", // 2
201                 "public class Class {", // 3
202                 "  @UnsupportedAppUsage(expectedSignature=\"expected\")", // 4
203                 "  public void method() {}", // 5
204                 "}");
205         Compilation compilation =
206                 Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
207                         .compile(ANNOTATION, src);
208         CompilationSubject.assertThat(compilation).failed();
209         CompilationSubject.assertThat(compilation).hadErrorContaining(
210                 "Expected signature doesn't match generated signature").inFile(src).onLine(5);
211     }
212 
213 }
214