/* * Copyright (C) 2015, 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 "ast_cpp.h" #include "code_writer.h" using std::string; using std::vector; using std::unique_ptr; namespace android { namespace aidl { namespace cpp { namespace { // clang-format off const char kExpectedHeaderOutput[] = R"(#pragma once #include #include namespace android { namespace test { class TestClass { public: void NormalMethod(int normalarg, float normal2); virtual void SubMethod(int subarg) const; }; // class TestClass class TestSubClass : public TestClass { public: virtual void SubMethod(int subarg) const; }; // class TestSubClass } // namespace test } // namespace android )"; const char kExpectedGenericHeaderOutput[] = R"(#pragma once #include #include namespace android { namespace test { template class TestParcelable : public ::android::Parcelable { public: int a; }; // class TestParcelable } // namespace test } // namespace android )"; // clang-format on const char kExpectedSwitchOutput[] = R"(switch (var) { case 2: { baz; } break; case 1: { foo; bar; } break; } )"; const char kExpectedMethodImplOutput[] = R"(return_type ClassName::MethodName(int32_t a, int32_t b, int32_t* c) const { foo; bar; } )"; const char kExpectedGenericMethodImplOutput[] = R"(template return_type ClassName::MethodName(int32_t a, int32_t b, int32_t* c) const { foo; bar; } )"; } // namespace class AstCppTests : public ::testing::Test { protected: void CompareGeneratedCode(const AstNode& node, const string& expected_output) { string actual_output; node.Write(CodeWriter::ForString(&actual_output).get()); EXPECT_EQ(expected_output, actual_output); } }; // class AstCppTests TEST_F(AstCppTests, GeneratesHeader) { unique_ptr norm{new MethodDecl( "void", "NormalMethod", ArgList{vector{"int normalarg", "float normal2"}})}; unique_ptr sub{ new MethodDecl("void", "SubMethod", ArgList{ "int subarg" }, MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)}; unique_ptr sub2{ new MethodDecl("void", "SubMethod", ArgList{ "int subarg" }, MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)}; vector> test_methods; test_methods.push_back(std::move(norm)); test_methods.push_back(std::move(sub)); vector> test_sub_methods; test_sub_methods.push_back(std::move(sub2)); unique_ptr test{new ClassDecl{"TestClass", "", {}, std::move(test_methods), {}}}; unique_ptr test_sub{ new ClassDecl{"TestSubClass", "TestClass", {}, std::move(test_sub_methods), {}}}; vector> classes; classes.push_back(std::move(test)); classes.push_back(std::move(test_sub)); unique_ptr test_ns{new CppNamespace {"test", std::move(classes)}}; vector> test_ns_vec; test_ns_vec.push_back(std::move(test_ns)); unique_ptr android_ns{new CppNamespace {"android", std::move(test_ns_vec) }}; vector> test_ns_globals; test_ns_globals.push_back(std::move(android_ns)); CppHeader cpp_header{{"string", "memory"}, std::move(test_ns_globals)}; CompareGeneratedCode(cpp_header, kExpectedHeaderOutput); } TEST_F(AstCppTests, GeneratesGenericHeader) { const std::vector type_params = {"A", "B"}; std::vector> publics; publics.emplace_back(new LiteralDecl("int a;\n")); unique_ptr test{new ClassDecl{ "TestParcelable", "::android::Parcelable", type_params, std::move(publics), {}}}; vector> classes; classes.push_back(std::move(test)); unique_ptr test_ns{new CppNamespace{"test", std::move(classes)}}; vector> test_ns_vec; test_ns_vec.push_back(std::move(test_ns)); unique_ptr android_ns{new CppNamespace{"android", std::move(test_ns_vec)}}; vector> test_ns_globals; test_ns_globals.push_back(std::move(android_ns)); CppHeader cpp_header{{"string", "memory"}, std::move(test_ns_globals)}; CompareGeneratedCode(cpp_header, kExpectedGenericHeaderOutput); } TEST_F(AstCppTests, GeneratesUnscopedEnum) { Enum e("Foo", "", false); e.AddValue("BAR", "42"); e.AddValue("BAZ", ""); string expected = R"(enum Foo { BAR = 42, BAZ, }; )"; CompareGeneratedCode(e, expected); } TEST_F(AstCppTests, GeneratesScopedEnum) { Enum e("Foo", "int32_t", true); e.AddValue("BAR", "42"); e.AddValue("BAZ", ""); string expected = R"(enum class Foo : int32_t { BAR = 42, BAZ, }; )"; CompareGeneratedCode(e, expected); } TEST_F(AstCppTests, GeneratesArgList) { ArgList simple("foo"); CompareGeneratedCode(simple, "(foo)"); ArgList compound({"foo", "bar", "baz"}); CompareGeneratedCode(compound, "(foo, bar, baz)"); std::vector> args; args.emplace_back(new LiteralExpression("foo()")); ArgList nested(std::move(args)); CompareGeneratedCode(nested, "(foo())"); } TEST_F(AstCppTests, GeneratesStatement) { Statement s(new LiteralExpression("foo")); CompareGeneratedCode(s, "foo;\n"); } TEST_F(AstCppTests, GeneratesComparison) { Comparison c( new LiteralExpression("lhs"), "&&", new LiteralExpression("rhs")); CompareGeneratedCode(c, "((lhs) && (rhs))"); } TEST_F(AstCppTests, GeneratesStatementBlock) { StatementBlock block; block.AddStatement(unique_ptr(new Statement("foo"))); block.AddStatement(unique_ptr(new Statement("bar"))); CompareGeneratedCode(block, "{\n foo;\n bar;\n}\n"); } TEST_F(AstCppTests, GeneratesConstructorImpl) { ConstructorImpl c("ClassName", ArgList({"a", "b", "c"}), {"baz_(foo)", "bar_(blah)"}); string expected = R"(ClassName::ClassName(a, b, c) : baz_(foo), bar_(blah){ } )"; CompareGeneratedCode(c, expected); } TEST_F(AstCppTests, GeneratesAssignment) { Assignment simple("foo", "8"); CompareGeneratedCode(simple, "foo = 8;\n"); Assignment less_simple("foo", new MethodCall("f", "8")); CompareGeneratedCode(less_simple, "foo = f(8);\n"); } TEST_F(AstCppTests, GeneratesMethodCall) { MethodCall single("single", "arg"); CompareGeneratedCode(single, "single(arg)"); MethodCall multi( "multi", ArgList({"has", "some", "args"})); CompareGeneratedCode(multi, "multi(has, some, args)"); } TEST_F(AstCppTests, GeneratesIfStatement) { IfStatement s(new LiteralExpression("foo")); s.OnTrue()->AddLiteral("on true1"); s.OnFalse()->AddLiteral("on false"); CompareGeneratedCode(s, "if (foo) {\n on true1;\n}\nelse {\n on false;\n}\n"); IfStatement s2(new LiteralExpression("bar")); s2.OnTrue()->AddLiteral("on true1"); CompareGeneratedCode(s2, "if (bar) {\n on true1;\n}\n"); } TEST_F(AstCppTests, GeneratesSwitchStatement) { SwitchStatement s("var"); // These are intentionally out of alphanumeric order. We're testing // that switch respects case addition order. auto case2 = s.AddCase("2"); case2->AddStatement(unique_ptr{new Statement{"baz"}}); auto case1 = s.AddCase("1"); case1->AddStatement(unique_ptr{new Statement{"foo"}}); case1->AddStatement(unique_ptr{new Statement{"bar"}}); CompareGeneratedCode(s, kExpectedSwitchOutput); } TEST_F(AstCppTests, GeneratesMethodImpl) { MethodImpl m{"return_type", "ClassName", "MethodName", {}, ArgList{{"int32_t a", "int32_t b", "int32_t* c"}}, true}; auto b = m.GetStatementBlock(); b->AddLiteral("foo"); b->AddLiteral("bar"); CompareGeneratedCode(m, kExpectedMethodImplOutput); } TEST_F(AstCppTests, GeneratesGenericMethodImpl) { MethodImpl m{"return_type", "ClassName", "MethodName", {"T"}, ArgList{{"int32_t a", "int32_t b", "int32_t* c"}}, true}; auto b = m.GetStatementBlock(); b->AddLiteral("foo"); b->AddLiteral("bar"); CompareGeneratedCode(m, kExpectedGenericMethodImplOutput); } TEST_F(AstCppTests, ToString) { std::string literal = "void foo() {}"; LiteralDecl decl(literal); std::string actual = decl.ToString(); EXPECT_EQ(literal, actual); std::string written; decl.Write(CodeWriter::ForString(&written).get()); EXPECT_EQ(literal, written); } } // namespace cpp } // namespace aidl } // namespace android