1 /*
2 * Copyright (C) 2020, 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 #include "diagnostics.h"
17
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <string>
21 #include <vector>
22
23 #include "aidl.h"
24 #include "parser.h"
25 #include "tests/fake_io_delegate.h"
26
27 using android::aidl::AidlTypenames;
28 using android::aidl::DiagnosticID;
29 using android::aidl::Options;
30 using android::aidl::internals::load_and_validate_aidl;
31 using android::aidl::test::FakeIoDelegate;
32 using testing::internal::CaptureStderr;
33 using testing::internal::GetCapturedStderr;
34
35 struct DiagnosticsTest : testing::Test {
ParseFilesDiagnosticsTest36 void ParseFiles(std::vector<std::pair<std::string, std::string>>&& files) {
37 ASSERT_TRUE(files.size() > 0);
38 const std::string main = files.begin()->first;
39 for (const auto& [file, contents] : files) {
40 io.SetFileContents(file, contents);
41 }
42 if (!enable_diagnostic) {
43 ASSERT_TRUE(expect_diagnostic);
44 enable_diagnostic = expect_diagnostic;
45 }
46 // emit diagnostics as warnings.
47 // "java" has no specific meaning here because we're testing CheckValid()
48 const Options options = Options::From("aidl " + optional_args + " -I . --lang java -o out -W" +
49 to_string(*enable_diagnostic) + " " + main);
50 CaptureStderr();
51 load_and_validate_aidl(main, options, io, &typenames, nullptr);
52 const std::string err = GetCapturedStderr();
53 if (expect_diagnostic) {
54 EXPECT_THAT(err, testing::HasSubstr("-W" + to_string(*expect_diagnostic)));
55 } else {
56 EXPECT_EQ("", err);
57 }
58 }
59
60 AidlTypenames typenames;
61 FakeIoDelegate io;
62 std::string optional_args;
63 // The type of diagnostic to enable for the test. If expect_diagnostic is
64 // set, use the same value.
65 std::optional<DiagnosticID> enable_diagnostic;
66 // The expected diagnostic. Must be set.
67 std::optional<DiagnosticID> expect_diagnostic;
68 };
69
TEST_F(DiagnosticsTest,const_name_ForEnumerator)70 TEST_F(DiagnosticsTest, const_name_ForEnumerator) {
71 expect_diagnostic = DiagnosticID::const_name;
72 ParseFiles({{"Foo.aidl", "enum Foo { foo }"}});
73 }
74
TEST_F(DiagnosticsTest,const_name_ForConstants)75 TEST_F(DiagnosticsTest, const_name_ForConstants) {
76 expect_diagnostic = DiagnosticID::const_name;
77 ParseFiles({{"IFoo.aidl", "interface IFoo { const int foo = 1; }"}});
78 }
79
TEST_F(DiagnosticsTest,interface_name)80 TEST_F(DiagnosticsTest, interface_name) {
81 expect_diagnostic = DiagnosticID::interface_name;
82 ParseFiles({{"Foo.aidl", "interface Foo { }"}});
83 }
84
TEST_F(DiagnosticsTest,enum_explicit_default)85 TEST_F(DiagnosticsTest, enum_explicit_default) {
86 expect_diagnostic = DiagnosticID::enum_explicit_default;
87 ParseFiles({{"Foo.aidl", "parcelable Foo { E e; }"}, {"E.aidl", "enum E { A }"}});
88 }
89
TEST_F(DiagnosticsTest,inout_parameter)90 TEST_F(DiagnosticsTest, inout_parameter) {
91 expect_diagnostic = DiagnosticID::inout_parameter;
92 ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(inout Bar bar); }"},
93 {"Bar.aidl", "parcelable Bar {}"}});
94 }
95
TEST_F(DiagnosticsTest,inout_parameter_SuppressAtMethodLevel)96 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtMethodLevel) {
97 enable_diagnostic = DiagnosticID::inout_parameter;
98 expect_diagnostic = {};
99 ParseFiles({
100 {"IFoo.aidl",
101 "interface IFoo { @SuppressWarnings(value={\"inout-parameter\"}) void foo(inout Bar b); }"},
102 {"Bar.aidl", "parcelable Bar {}"},
103 });
104 }
105
TEST_F(DiagnosticsTest,inout_parameter_SuppressAtDeclLevel)106 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtDeclLevel) {
107 enable_diagnostic = DiagnosticID::inout_parameter;
108 expect_diagnostic = {};
109 ParseFiles({
110 {"IFoo.aidl",
111 "@SuppressWarnings(value={\"inout-parameter\"}) interface IFoo { void foo(inout Bar b); }"},
112 {"Bar.aidl", "parcelable Bar {}"},
113 });
114 }
115
TEST_F(DiagnosticsTest,UnknownWarning)116 TEST_F(DiagnosticsTest, UnknownWarning) {
117 expect_diagnostic = DiagnosticID::unknown_warning;
118 ParseFiles({
119 {"IFoo.aidl", "@SuppressWarnings(value={\"blahblah\"}) interface IFoo { void foo(); }"},
120 });
121 }
122
TEST_F(DiagnosticsTest,CantSuppressUnknownWarning)123 TEST_F(DiagnosticsTest, CantSuppressUnknownWarning) {
124 expect_diagnostic = DiagnosticID::unknown_warning;
125 ParseFiles({
126 {"IFoo.aidl",
127 "@SuppressWarnings(value={\"unknown-warning\"})\n"
128 "interface IFoo { @SuppressWarnings(value={\"blah-blah\"}) void foo(); }"},
129 });
130 }
131
TEST_F(DiagnosticsTest,DontMixOnewayWithTwowayMethods)132 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethods) {
133 expect_diagnostic = DiagnosticID::mixed_oneway;
134 ParseFiles({
135 {"IFoo.aidl", "interface IFoo { void foo(); oneway void bar(); }"},
136 });
137 }
138
TEST_F(DiagnosticsTest,DontMixOnewayWithTwowayMethodsSuppressedAtMethod)139 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethodsSuppressedAtMethod) {
140 enable_diagnostic = DiagnosticID::mixed_oneway;
141 expect_diagnostic = {};
142 ParseFiles({
143 {"IFoo.aidl",
144 "interface IFoo {\n"
145 " void foo();\n"
146 " @SuppressWarnings(value={\"mixed-oneway\"}) oneway void bar();\n"
147 "}"},
148 });
149 }
150
TEST_F(DiagnosticsTest,OnewayInterfaceIsOkayWithSyntheticMethods)151 TEST_F(DiagnosticsTest, OnewayInterfaceIsOkayWithSyntheticMethods) {
152 optional_args = "--version 2"; // will add getInterfaceVersion() synthetic method
153 enable_diagnostic = DiagnosticID::mixed_oneway;
154 expect_diagnostic = {};
155 ParseFiles({
156 {"IFoo.aidl", "oneway interface IFoo { void foo(); }"},
157 });
158 }
159
TEST_F(DiagnosticsTest,ArraysAsOutputParametersConsideredHarmful)160 TEST_F(DiagnosticsTest, ArraysAsOutputParametersConsideredHarmful) {
161 expect_diagnostic = DiagnosticID::out_array;
162 ParseFiles({
163 {"IFoo.aidl", "interface IFoo { void foo(out String[] ret); }"},
164 });
165 }
166
TEST_F(DiagnosticsTest,file_descriptor)167 TEST_F(DiagnosticsTest, file_descriptor) {
168 expect_diagnostic = DiagnosticID::file_descriptor;
169 ParseFiles({{"IFoo.aidl",
170 "interface IFoo {\n"
171 " void foo(in FileDescriptor fd);\n"
172 "}"}});
173 }
174
TEST_F(DiagnosticsTest,out_nullable)175 TEST_F(DiagnosticsTest, out_nullable) {
176 expect_diagnostic = DiagnosticID::out_nullable;
177 ParseFiles({{"IFoo.aidl",
178 "interface IFoo {\n"
179 " void foo(out @nullable Bar bar);\n"
180 "}"},
181 {"Bar.aidl", "parcelable Bar {}"}});
182 }
183
TEST_F(DiagnosticsTest,inout_nullable)184 TEST_F(DiagnosticsTest, inout_nullable) {
185 expect_diagnostic = DiagnosticID::out_nullable;
186 ParseFiles({{"IFoo.aidl",
187 "interface IFoo {\n"
188 " void foo(inout @nullable Bar bar);\n"
189 "}"},
190 {"Bar.aidl", "parcelable Bar {}"}});
191 }
192
TEST_F(DiagnosticsTest,out_nullable_OkayForArrays)193 TEST_F(DiagnosticsTest, out_nullable_OkayForArrays) {
194 expect_diagnostic = DiagnosticID::out_array; // not triggering out_nullable
195 ParseFiles({{"IFoo.aidl",
196 "interface IFoo {\n"
197 " void foo(inout @nullable Bar[] bar1, out @nullable Bar[] bar2);\n"
198 "}"},
199 {"Bar.aidl", "parcelable Bar {}"}});
200 }
201
TEST_F(DiagnosticsTest,RejectImportsCollisionWithTopLevelDecl)202 TEST_F(DiagnosticsTest, RejectImportsCollisionWithTopLevelDecl) {
203 expect_diagnostic = DiagnosticID::unique_import;
204 ParseFiles({{"p/IFoo.aidl",
205 "package p;\n"
206 "import q.IFoo;\n" // should collide with previous import
207 "interface IFoo{}"},
208 {"q/IFoo.aidl", "package q; interface IFoo{}"}});
209 }
210
TEST_F(DiagnosticsTest,RejectImportsCollision)211 TEST_F(DiagnosticsTest, RejectImportsCollision) {
212 expect_diagnostic = DiagnosticID::unique_import;
213 ParseFiles({{"p/IFoo.aidl",
214 "package p;\n"
215 "import q.IBar;\n"
216 "import r.IBar;\n" // should collide with previous import
217 "interface IFoo{}"},
218 {"q/IBar.aidl", "package q; interface IBar{}"},
219 {"r/IBar.aidl", "package r; interface IBar{}"}});
220 }
221
TEST_F(DiagnosticsTest,AllowImportingSelf)222 TEST_F(DiagnosticsTest, AllowImportingSelf) {
223 enable_diagnostic = DiagnosticID::unique_import;
224 expect_diagnostic = {};
225 ParseFiles({{"p/IFoo.aidl",
226 "package p;\n"
227 "import p.IFoo;\n"
228 "interface IFoo{}"}});
229 }
230
TEST_F(DiagnosticsTest,RedundantImports)231 TEST_F(DiagnosticsTest, RedundantImports) {
232 expect_diagnostic = DiagnosticID::unique_import;
233 ParseFiles({{"p/IFoo.aidl",
234 "package p;\n"
235 "import q.IBar;\n"
236 "import q.IBar;\n"
237 "interface IFoo{}"},
238 {"q/IBar.aidl", "package q; interface IBar{}"}});
239 }
240
TEST_F(DiagnosticsTest,UntypedCollectionInterface)241 TEST_F(DiagnosticsTest, UntypedCollectionInterface) {
242 expect_diagnostic = DiagnosticID::untyped_collection;
243 ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Map m); }"}});
244 }
245
TEST_F(DiagnosticsTest,UntypedCollectionParcelable)246 TEST_F(DiagnosticsTest, UntypedCollectionParcelable) {
247 expect_diagnostic = DiagnosticID::untyped_collection;
248 ParseFiles({{"Foo.aidl", "parcelable Foo { Map m; }"}});
249 }
250
TEST_F(DiagnosticsTest,UntypedCollectionUnion)251 TEST_F(DiagnosticsTest, UntypedCollectionUnion) {
252 expect_diagnostic = DiagnosticID::untyped_collection;
253 ParseFiles({{"Foo.aidl", "union Foo { List l; }"}});
254 }
255
TEST_F(DiagnosticsTest,UntypedCollectionInTypeArg)256 TEST_F(DiagnosticsTest, UntypedCollectionInTypeArg) {
257 expect_diagnostic = DiagnosticID::untyped_collection;
258 ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Bar<Map> m); }"},
259 {"Bar.aidl", "parcelable Bar<T> {}"}});
260 }
261
TEST_F(DiagnosticsTest,PermissionMissing)262 TEST_F(DiagnosticsTest, PermissionMissing) {
263 expect_diagnostic = DiagnosticID::missing_permission_annotation;
264 ParseFiles({{"IFoo.aidl", "interface IFoo { void food(); }"}});
265 }
266
TEST_F(DiagnosticsTest,PermissionMethod)267 TEST_F(DiagnosticsTest, PermissionMethod) {
268 enable_diagnostic = DiagnosticID::missing_permission_annotation;
269 expect_diagnostic = {};
270 ParseFiles({{"IFoo.aidl", "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); }"}});
271 }
272
TEST_F(DiagnosticsTest,PermissionMethodMissing)273 TEST_F(DiagnosticsTest, PermissionMethodMissing) {
274 expect_diagnostic = DiagnosticID::missing_permission_annotation;
275 ParseFiles({{"IFoo.aidl",
276 "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); void foo2(); }"}});
277 }
278
TEST_F(DiagnosticsTest,PermissionInterface)279 TEST_F(DiagnosticsTest, PermissionInterface) {
280 enable_diagnostic = DiagnosticID::missing_permission_annotation;
281 expect_diagnostic = {};
282 ParseFiles({{"IFoo.aidl", "@EnforcePermission(\"INTERNET\") interface IFoo { void food(); }"}});
283 }
284