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 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <hidl-util/StringHelper.h>
20
21 #include <string>
22 #include <vector>
23
24 #include "../Lint.h"
25 #include "../LintRegistry.h"
26 #include "Coordinator.h"
27
28 using ::testing::Contains;
29 using ::testing::ContainsRegex;
30 using ::testing::Property;
31
32 namespace android {
33 class HidlLintTest : public ::testing::Test {
34 protected:
35 Coordinator coordinator;
36
SetUp()37 void SetUp() override {
38 char* argv[2] = {const_cast<char*>("hidl-lint"),
39 const_cast<char*>("-rlint_test:system/tools/hidl/lint/test/interfaces")};
40 coordinator.parseOptions(2, argv, "", [](int /* opt */, char* /* optarg */) {});
41 }
42
getLintsForHal(const std::string & name,std::vector<Lint> * errors)43 void getLintsForHal(const std::string& name, std::vector<Lint>* errors) {
44 std::vector<FQName> targets;
45
46 FQName fqName;
47 if (!FQName::parse(name, &fqName)) {
48 FAIL() << "Could not parse fqName: " << name;
49 }
50
51 if (fqName.isFullyQualified()) {
52 targets.push_back(fqName);
53 } else {
54 status_t err = coordinator.appendPackageInterfacesToVector(fqName, &targets);
55 if (err != OK) {
56 FAIL() << "Could not get sources for: " << name;
57 }
58 }
59
60 for (const FQName& fqName : targets) {
61 AST* ast = coordinator.parse(fqName);
62 if (ast == nullptr) {
63 FAIL() << "Could not parse " << fqName.name() << ". Aborting.";
64 }
65
66 LintRegistry::get()->runAllLintFunctions(*ast, errors);
67 }
68 }
69 };
70
71 #define EXPECT_NO_LINT(interface) \
72 do { \
73 std::vector<Lint> errors; \
74 getLintsForHal(interface, &errors); \
75 EXPECT_EQ(0, errors.size()); \
76 } while (false)
77
78 #define EXPECT_LINT(interface, errorMsg) \
79 do { \
80 std::vector<Lint> errors; \
81 getLintsForHal(interface, &errors); \
82 EXPECT_EQ(1, errors.size()); \
83 if (errors.size() != 1) break; \
84 EXPECT_THAT(errors[0].getMessage(), ContainsRegex(errorMsg)); \
85 } while (false)
86
87 #define EXPECT_A_LINT(interface, errorMsg) \
88 do { \
89 std::vector<Lint> errors; \
90 getLintsForHal(interface, &errors); \
91 EXPECT_LE(1, errors.size()); \
92 if (errors.size() < 1) break; \
93 EXPECT_THAT(errors, Contains(Property(&Lint::getMessage, ContainsRegex(errorMsg)))); \
94 } while (false)
95
TEST_F(HidlLintTest,OnewayLintTest)96 TEST_F(HidlLintTest, OnewayLintTest) {
97 // Has no errors (empty). Lint size should be 0.
98 EXPECT_NO_LINT("lint_test.oneway@1.0::IEmpty");
99
100 // Only has either oneway or nononeway methods. Lint size should be 0.
101 EXPECT_NO_LINT("lint_test.oneway@1.0::IOneway");
102 EXPECT_NO_LINT("lint_test.oneway@1.0::INonOneway");
103
104 // A child of a mixed interface should not trigger a lint if it is oneway/nononeway.
105 // Lint size should be 0
106 EXPECT_NO_LINT("lint_test.oneway@1.0::IMixedOnewayChild");
107 EXPECT_NO_LINT("lint_test.oneway@1.0::IMixedNonOnewayChild");
108
109 // A child with the same oneway type should not trigger a lint. Lint size should be 0.
110 EXPECT_NO_LINT("lint_test.oneway@1.0::IOnewayChild");
111 EXPECT_NO_LINT("lint_test.oneway@1.0::INonOnewayChild");
112
113 // This interface is mixed. Should have a lint.
114 EXPECT_LINT("lint_test.oneway@1.0::IMixed", "IMixed has both oneway and non-oneway methods.");
115
116 // Regardless of parent, if interface is mixed, it should have a lint.
117 EXPECT_LINT("lint_test.oneway@1.0::IMixedMixedChild",
118 "IMixedMixedChild has both oneway and non-oneway methods.");
119
120 // When onewaytype is different from parent it should trigger a lint.
121 EXPECT_LINT("lint_test.oneway@1.0::IOnewayOpposite",
122 "IOnewayOpposite should only have oneway methods");
123
124 EXPECT_LINT("lint_test.oneway@1.0::INonOnewayOpposite",
125 "INonOnewayOpposite should only have non-oneway methods");
126 }
127
TEST_F(HidlLintTest,SafeunionLintTest)128 TEST_F(HidlLintTest, SafeunionLintTest) {
129 // Has no errors (empty). Even though types.hal has a lint.
130 EXPECT_NO_LINT("lint_test.safeunion@1.0::IEmpty");
131
132 // A child of an interface that refers to a union should not lint unless it refers to a union
133 EXPECT_NO_LINT("lint_test.safeunion@1.1::IReference");
134
135 // Should lint the union type definition
136 EXPECT_LINT("lint_test.safeunion@1.0::types", "union InTypes.*defined");
137 EXPECT_LINT("lint_test.safeunion@1.0::IDefined", "union SomeUnion.*defined");
138
139 // Should mention that a union type is being referenced and where that type is.
140 EXPECT_LINT("lint_test.safeunion@1.0::IReference", "Reference to union type.*types.hal");
141
142 // Referencing a union inside a struct should lint
143 EXPECT_LINT("lint_test.safeunion@1.1::types", "Reference to union type.*1\\.0/types.hal");
144
145 // Defining a union inside a struct should lint
146 EXPECT_LINT("lint_test.safeunion@1.0::IUnionInStruct", "union SomeUnionInStruct.*defined");
147
148 // Reference to a struct that contains a union should lint
149 EXPECT_LINT("lint_test.safeunion@1.1::IReferStructWithUnion",
150 "Reference to struct.*contains a union type.");
151 }
152
TEST_F(HidlLintTest,ImportTypesTest)153 TEST_F(HidlLintTest, ImportTypesTest) {
154 // Imports types.hal file from package
155 EXPECT_LINT("lint_test.import_types@1.0::IImport", "Redundant import");
156
157 // Imports types.hal from other package
158 EXPECT_LINT("lint_test.import_types@1.0::IImportOther", "This imports every type");
159
160 // Imports types.hal from previous version of the same package
161 EXPECT_LINT("lint_test.import_types@1.1::types", "This imports every type");
162
163 // Imports types.hal from same package with fully qualified name
164 EXPECT_LINT("lint_test.import_types@1.1::IImport", "Redundant import");
165 }
166
TEST_F(HidlLintTest,SmallStructsTest)167 TEST_F(HidlLintTest, SmallStructsTest) {
168 // Referencing bad structs should not lint
169 EXPECT_NO_LINT("lint_test.small_structs@1.0::IReference");
170
171 // Empty structs/unions should lint
172 EXPECT_LINT("lint_test.small_structs@1.0::IEmptyStruct", "contains no elements");
173 EXPECT_A_LINT("lint_test.small_structs@1.0::IEmptyUnion", "contains no elements");
174
175 // Structs/unions with single field should lint
176 EXPECT_LINT("lint_test.small_structs@1.0::ISingleStruct", "only contains 1 element");
177 EXPECT_A_LINT("lint_test.small_structs@1.0::ISingleUnion", "only contains 1 element");
178 }
179
TEST_F(HidlLintTest,DocCommentRefTest)180 TEST_F(HidlLintTest, DocCommentRefTest) {
181 EXPECT_NO_LINT("lint_test.doc_comments@1.0::ICorrect");
182
183 // Should lint since nothing follows the keyword
184 EXPECT_LINT("lint_test.doc_comments@1.0::INoReturn",
185 "should be followed by a return parameter");
186 EXPECT_LINT("lint_test.doc_comments@1.0::INoParam", "should be followed by a parameter name");
187 EXPECT_LINT("lint_test.doc_comments@1.0::IReturnSpace",
188 "should be followed by a return parameter");
189
190 // Typos should be caught
191 EXPECT_LINT("lint_test.doc_comments@1.0::IWrongReturn", "is not a return parameter");
192 EXPECT_LINT("lint_test.doc_comments@1.0::IWrongParam", "is not an argument");
193
194 // Incorrectly marked as @param should lint as a param
195 EXPECT_LINT("lint_test.doc_comments@1.0::ISwitched", "is not an argument");
196
197 // Incorrectly marked as @param should lint as a param
198 EXPECT_LINT("lint_test.doc_comments@1.0::IParamAfterReturn",
199 "@param references should come before @return");
200
201 // Reversed order should be caught
202 EXPECT_LINT("lint_test.doc_comments@1.0::IRevReturn",
203 "@return references should be ordered the same way they show up");
204 EXPECT_LINT("lint_test.doc_comments@1.0::IRevParam",
205 "@param references should be ordered the same way they show up");
206
207 // Referencing the same param/return multiple times should be caught
208 EXPECT_LINT("lint_test.doc_comments@1.0::IDoubleReturn", "was referenced multiple times");
209 EXPECT_LINT("lint_test.doc_comments@1.0::IDoubleParam", "was referenced multiple times");
210 }
211
TEST_F(HidlLintTest,MethodVersionsTest)212 TEST_F(HidlLintTest, MethodVersionsTest) {
213 // Extends baseMethod correctly
214 EXPECT_NO_LINT("lint_test.method_versions@1.0::IChangeBase");
215
216 // Extends IBase.foo through @1.0::IChangeBase correctly
217 EXPECT_NO_LINT("lint_test.method_versions@1.1::IChangeBase");
218
219 // Lints because lintBadName_V1_x is not minor_major version naming
220 EXPECT_LINT("lint_test.method_versions@1.0::IBase",
221 "Methods should follow the camelCase naming convention.");
222
223 // Lints because incorrect package name
224 EXPECT_LINT("lint_test.method_versions@1.0::IChild", "interface is in package version 1.0");
225
226 // Lints because wrong minor version
227 EXPECT_LINT("lint_test.method_versions@1.0::IWrongMinor",
228 "Methods should follow the camelCase naming convention.");
229
230 // Lints because underscore in wrong place
231 EXPECT_LINT("lint_test.method_versions@1.0::IWrongUnderscore",
232 "when defining a new version of a method");
233
234 // Method does not exist in any of the super types
235 EXPECT_LINT("lint_test.method_versions@1.1::IMethodDNE", "Could not find method");
236
237 // Methods are not in camel case
238 EXPECT_LINT("lint_test.method_versions@1.0::IPascalCase",
239 "Methods should follow the camelCase naming convention.");
240 EXPECT_LINT("lint_test.method_versions@1.0::IHybrid",
241 "Methods should follow the camelCase naming convention.");
242 EXPECT_LINT("lint_test.method_versions@1.0::ISnakeCase",
243 "Methods should follow the camelCase naming convention.");
244 }
245
TEST_F(HidlLintTest,EnumMaxAllTest)246 TEST_F(HidlLintTest, EnumMaxAllTest) {
247 // Implements MAX correctly
248 EXPECT_NO_LINT("lint_test.enum_max_all@1.0::IFoo");
249
250 // Lint since MAX and ALL are enum values
251 EXPECT_LINT("lint_test.enum_max_all@1.0::IMax",
252 "\"MAX\" enum values have been known to become out of date");
253 EXPECT_LINT("lint_test.enum_max_all@1.0::IAll",
254 "\"ALL\" enum values have been known to become out of date");
255 EXPECT_LINT("lint_test.enum_max_all@1.0::ICount",
256 "\"COUNT\" enum values have been known to become out of date");
257
258 // Lint since MAX and ALL are parts of the enum values
259 EXPECT_LINT("lint_test.enum_max_all@1.0::IMax2",
260 "\"MAX\" enum values have been known to become out of date");
261 EXPECT_LINT("lint_test.enum_max_all@1.0::IAll2",
262 "\"ALL\" enum values have been known to become out of date");
263 EXPECT_LINT("lint_test.enum_max_all@1.0::ICount2",
264 "\"COUNT\" enum values have been known to become out of date");
265 }
266
TEST_F(HidlLintTest,UnhandledDocCommentTest)267 TEST_F(HidlLintTest, UnhandledDocCommentTest) {
268 EXPECT_LINT("lint_test.unhandled_comments@1.0::types",
269 "cannot be processed since it is in an unrecognized place");
270
271 // Even single line comments are unhandled
272 EXPECT_LINT("lint_test.unhandled_comments@1.0::ISingleComment",
273 "cannot be processed since it is in an unrecognized place");
274 }
275
TEST_F(HidlLintTest,NamingConventionsTest)276 TEST_F(HidlLintTest, NamingConventionsTest) {
277 EXPECT_LINT("lint_test.naming_conventions@1.0::IBad_Interface",
278 "type .* should be named .* PascalCase");
279 EXPECT_LINT("lint_test.naming_conventions@1.0::IBadStruct",
280 "type .* should be named .* PascalCase");
281 EXPECT_LINT("lint_test.naming_conventions@1.0::IBadEnum",
282 "type .* should be named .* PascalCase");
283 EXPECT_A_LINT("lint_test.naming_conventions@1.0::IBadUnion",
284 "type .* should be named .* PascalCase");
285
286 EXPECT_LINT("lint_test.naming_conventions@1.0::IBadStructMember",
287 "member .* of type .* should be named .* camelCase");
288 EXPECT_A_LINT("lint_test.naming_conventions@1.0::IBadUnionMember",
289 "member .* of type .* should be named .* camelCase");
290
291 EXPECT_LINT("lint_test.naming_conventions@1.0::IBadEnumValue",
292 "enumeration .* of enum .* should be named .* UPPER_SNAKE_CASE");
293 }
294 } // namespace android
295