/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "../Lint.h" #include "../LintRegistry.h" #include "Coordinator.h" using ::testing::Contains; using ::testing::ContainsRegex; using ::testing::Property; namespace android { class HidlLintTest : public ::testing::Test { protected: Coordinator coordinator; void SetUp() override { char* argv[2] = {const_cast("hidl-lint"), const_cast("-rlint_test:system/tools/hidl/lint/test/interfaces")}; coordinator.parseOptions(2, argv, "", [](int /* opt */, char* /* optarg */) {}); } void getLintsForHal(const std::string& name, std::vector* errors) { std::vector targets; FQName fqName; if (!FQName::parse(name, &fqName)) { FAIL() << "Could not parse fqName: " << name; } if (fqName.isFullyQualified()) { targets.push_back(fqName); } else { status_t err = coordinator.appendPackageInterfacesToVector(fqName, &targets); if (err != OK) { FAIL() << "Could not get sources for: " << name; } } for (const FQName& fqName : targets) { AST* ast = coordinator.parse(fqName); if (ast == nullptr) { FAIL() << "Could not parse " << fqName.name() << ". Aborting."; } LintRegistry::get()->runAllLintFunctions(*ast, errors); } } }; #define EXPECT_NO_LINT(interface) \ do { \ std::vector errors; \ getLintsForHal(interface, &errors); \ EXPECT_EQ(0, errors.size()); \ } while (false) #define EXPECT_LINT(interface, errorMsg) \ do { \ std::vector errors; \ getLintsForHal(interface, &errors); \ EXPECT_EQ(1, errors.size()); \ if (errors.size() != 1) break; \ EXPECT_THAT(errors[0].getMessage(), ContainsRegex(errorMsg)); \ } while (false) #define EXPECT_A_LINT(interface, errorMsg) \ do { \ std::vector errors; \ getLintsForHal(interface, &errors); \ EXPECT_LE(1, errors.size()); \ if (errors.size() < 1) break; \ EXPECT_THAT(errors, Contains(Property(&Lint::getMessage, ContainsRegex(errorMsg)))); \ } while (false) TEST_F(HidlLintTest, OnewayLintTest) { // Has no errors (empty). Lint size should be 0. EXPECT_NO_LINT("lint_test.oneway@1.0::IEmpty"); // Only has either oneway or nononeway methods. Lint size should be 0. EXPECT_NO_LINT("lint_test.oneway@1.0::IOneway"); EXPECT_NO_LINT("lint_test.oneway@1.0::INonOneway"); // A child of a mixed interface should not trigger a lint if it is oneway/nononeway. // Lint size should be 0 EXPECT_NO_LINT("lint_test.oneway@1.0::IMixedOnewayChild"); EXPECT_NO_LINT("lint_test.oneway@1.0::IMixedNonOnewayChild"); // A child with the same oneway type should not trigger a lint. Lint size should be 0. EXPECT_NO_LINT("lint_test.oneway@1.0::IOnewayChild"); EXPECT_NO_LINT("lint_test.oneway@1.0::INonOnewayChild"); // This interface is mixed. Should have a lint. EXPECT_LINT("lint_test.oneway@1.0::IMixed", "IMixed has both oneway and non-oneway methods."); // Regardless of parent, if interface is mixed, it should have a lint. EXPECT_LINT("lint_test.oneway@1.0::IMixedMixedChild", "IMixedMixedChild has both oneway and non-oneway methods."); // When onewaytype is different from parent it should trigger a lint. EXPECT_LINT("lint_test.oneway@1.0::IOnewayOpposite", "IOnewayOpposite should only have oneway methods"); EXPECT_LINT("lint_test.oneway@1.0::INonOnewayOpposite", "INonOnewayOpposite should only have non-oneway methods"); } TEST_F(HidlLintTest, SafeunionLintTest) { // Has no errors (empty). Even though types.hal has a lint. EXPECT_NO_LINT("lint_test.safeunion@1.0::IEmpty"); // A child of an interface that refers to a union should not lint unless it refers to a union EXPECT_NO_LINT("lint_test.safeunion@1.1::IReference"); // Should lint the union type definition EXPECT_LINT("lint_test.safeunion@1.0::types", "union InTypes.*defined"); EXPECT_LINT("lint_test.safeunion@1.0::IDefined", "union SomeUnion.*defined"); // Should mention that a union type is being referenced and where that type is. EXPECT_LINT("lint_test.safeunion@1.0::IReference", "Reference to union type.*types.hal"); // Referencing a union inside a struct should lint EXPECT_LINT("lint_test.safeunion@1.1::types", "Reference to union type.*1\\.0/types.hal"); // Defining a union inside a struct should lint EXPECT_LINT("lint_test.safeunion@1.0::IUnionInStruct", "union SomeUnionInStruct.*defined"); // Reference to a struct that contains a union should lint EXPECT_LINT("lint_test.safeunion@1.1::IReferStructWithUnion", "Reference to struct.*contains a union type."); } TEST_F(HidlLintTest, ImportTypesTest) { // Imports types.hal file from package EXPECT_LINT("lint_test.import_types@1.0::IImport", "Redundant import"); // Imports types.hal from other package EXPECT_LINT("lint_test.import_types@1.0::IImportOther", "This imports every type"); // Imports types.hal from previous version of the same package EXPECT_LINT("lint_test.import_types@1.1::types", "This imports every type"); // Imports types.hal from same package with fully qualified name EXPECT_LINT("lint_test.import_types@1.1::IImport", "Redundant import"); } TEST_F(HidlLintTest, SmallStructsTest) { // Referencing bad structs should not lint EXPECT_NO_LINT("lint_test.small_structs@1.0::IReference"); // Empty structs/unions should lint EXPECT_LINT("lint_test.small_structs@1.0::IEmptyStruct", "contains no elements"); EXPECT_A_LINT("lint_test.small_structs@1.0::IEmptyUnion", "contains no elements"); // Structs/unions with single field should lint EXPECT_LINT("lint_test.small_structs@1.0::ISingleStruct", "only contains 1 element"); EXPECT_A_LINT("lint_test.small_structs@1.0::ISingleUnion", "only contains 1 element"); } TEST_F(HidlLintTest, DocCommentRefTest) { EXPECT_NO_LINT("lint_test.doc_comments@1.0::ICorrect"); // Should lint since nothing follows the keyword EXPECT_LINT("lint_test.doc_comments@1.0::INoReturn", "should be followed by a return parameter"); EXPECT_LINT("lint_test.doc_comments@1.0::INoParam", "should be followed by a parameter name"); EXPECT_LINT("lint_test.doc_comments@1.0::IReturnSpace", "should be followed by a return parameter"); // Typos should be caught EXPECT_LINT("lint_test.doc_comments@1.0::IWrongReturn", "is not a return parameter"); EXPECT_LINT("lint_test.doc_comments@1.0::IWrongParam", "is not an argument"); // Incorrectly marked as @param should lint as a param EXPECT_LINT("lint_test.doc_comments@1.0::ISwitched", "is not an argument"); // Incorrectly marked as @param should lint as a param EXPECT_LINT("lint_test.doc_comments@1.0::IParamAfterReturn", "@param references should come before @return"); // Reversed order should be caught EXPECT_LINT("lint_test.doc_comments@1.0::IRevReturn", "@return references should be ordered the same way they show up"); EXPECT_LINT("lint_test.doc_comments@1.0::IRevParam", "@param references should be ordered the same way they show up"); // Referencing the same param/return multiple times should be caught EXPECT_LINT("lint_test.doc_comments@1.0::IDoubleReturn", "was referenced multiple times"); EXPECT_LINT("lint_test.doc_comments@1.0::IDoubleParam", "was referenced multiple times"); } TEST_F(HidlLintTest, MethodVersionsTest) { // Extends baseMethod correctly EXPECT_NO_LINT("lint_test.method_versions@1.0::IChangeBase"); // Extends IBase.foo through @1.0::IChangeBase correctly EXPECT_NO_LINT("lint_test.method_versions@1.1::IChangeBase"); // Lints because lintBadName_V1_x is not minor_major version naming EXPECT_LINT("lint_test.method_versions@1.0::IBase", "Methods should follow the camelCase naming convention."); // Lints because incorrect package name EXPECT_LINT("lint_test.method_versions@1.0::IChild", "interface is in package version 1.0"); // Lints because wrong minor version EXPECT_LINT("lint_test.method_versions@1.0::IWrongMinor", "Methods should follow the camelCase naming convention."); // Lints because underscore in wrong place EXPECT_LINT("lint_test.method_versions@1.0::IWrongUnderscore", "when defining a new version of a method"); // Method does not exist in any of the super types EXPECT_LINT("lint_test.method_versions@1.1::IMethodDNE", "Could not find method"); // Methods are not in camel case EXPECT_LINT("lint_test.method_versions@1.0::IPascalCase", "Methods should follow the camelCase naming convention."); EXPECT_LINT("lint_test.method_versions@1.0::IHybrid", "Methods should follow the camelCase naming convention."); EXPECT_LINT("lint_test.method_versions@1.0::ISnakeCase", "Methods should follow the camelCase naming convention."); } TEST_F(HidlLintTest, EnumMaxAllTest) { // Implements MAX correctly EXPECT_NO_LINT("lint_test.enum_max_all@1.0::IFoo"); // Lint since MAX and ALL are enum values EXPECT_LINT("lint_test.enum_max_all@1.0::IMax", "\"MAX\" enum values have been known to become out of date"); EXPECT_LINT("lint_test.enum_max_all@1.0::IAll", "\"ALL\" enum values have been known to become out of date"); EXPECT_LINT("lint_test.enum_max_all@1.0::ICount", "\"COUNT\" enum values have been known to become out of date"); // Lint since MAX and ALL are parts of the enum values EXPECT_LINT("lint_test.enum_max_all@1.0::IMax2", "\"MAX\" enum values have been known to become out of date"); EXPECT_LINT("lint_test.enum_max_all@1.0::IAll2", "\"ALL\" enum values have been known to become out of date"); EXPECT_LINT("lint_test.enum_max_all@1.0::ICount2", "\"COUNT\" enum values have been known to become out of date"); } TEST_F(HidlLintTest, UnhandledDocCommentTest) { EXPECT_LINT("lint_test.unhandled_comments@1.0::types", "cannot be processed since it is in an unrecognized place"); // Even single line comments are unhandled EXPECT_LINT("lint_test.unhandled_comments@1.0::ISingleComment", "cannot be processed since it is in an unrecognized place"); } TEST_F(HidlLintTest, NamingConventionsTest) { EXPECT_LINT("lint_test.naming_conventions@1.0::IBad_Interface", "type .* should be named .* PascalCase"); EXPECT_LINT("lint_test.naming_conventions@1.0::IBadStruct", "type .* should be named .* PascalCase"); EXPECT_LINT("lint_test.naming_conventions@1.0::IBadEnum", "type .* should be named .* PascalCase"); EXPECT_A_LINT("lint_test.naming_conventions@1.0::IBadUnion", "type .* should be named .* PascalCase"); EXPECT_LINT("lint_test.naming_conventions@1.0::IBadStructMember", "member .* of type .* should be named .* camelCase"); EXPECT_A_LINT("lint_test.naming_conventions@1.0::IBadUnionMember", "member .* of type .* should be named .* camelCase"); EXPECT_LINT("lint_test.naming_conventions@1.0::IBadEnumValue", "enumeration .* of enum .* should be named .* UPPER_SNAKE_CASE"); } } // namespace android