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