| /* |
| * 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 <string> |
| |
| #include <gtest/gtest.h> |
| |
| #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 <string> |
| #include <memory> |
| |
| 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 <string> |
| #include <memory> |
| |
| namespace android { |
| |
| namespace test { |
| |
| template <typename A, typename B> |
| 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 <typename T> |
| return_type ClassName<T>::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<MethodDecl> norm{new MethodDecl( |
| "void", "NormalMethod", |
| ArgList{vector<string>{"int normalarg", "float normal2"}})}; |
| unique_ptr<MethodDecl> sub{ |
| new MethodDecl("void", "SubMethod", |
| ArgList{ "int subarg" }, |
| MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)}; |
| unique_ptr<MethodDecl> sub2{ |
| new MethodDecl("void", "SubMethod", |
| ArgList{ "int subarg" }, |
| MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)}; |
| vector<unique_ptr<Declaration>> test_methods; |
| test_methods.push_back(std::move(norm)); |
| test_methods.push_back(std::move(sub)); |
| |
| vector<unique_ptr<Declaration>> test_sub_methods; |
| test_sub_methods.push_back(std::move(sub2)); |
| |
| unique_ptr<Declaration> test{new ClassDecl{"TestClass", "", {}, std::move(test_methods), {}}}; |
| |
| unique_ptr<Declaration> test_sub{ |
| new ClassDecl{"TestSubClass", "TestClass", {}, std::move(test_sub_methods), {}}}; |
| |
| vector<unique_ptr<Declaration>> classes; |
| classes.push_back(std::move(test)); |
| classes.push_back(std::move(test_sub)); |
| |
| unique_ptr<CppNamespace> test_ns{new CppNamespace {"test", |
| std::move(classes)}}; |
| |
| vector<unique_ptr<Declaration>> test_ns_vec; |
| test_ns_vec.push_back(std::move(test_ns)); |
| |
| unique_ptr<CppNamespace> android_ns{new CppNamespace {"android", |
| std::move(test_ns_vec) }}; |
| |
| vector<unique_ptr<Declaration>> 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<std::string> type_params = {"A", "B"}; |
| std::vector<std::unique_ptr<Declaration>> publics; |
| publics.emplace_back(new LiteralDecl("int a;\n")); |
| unique_ptr<Declaration> test{new ClassDecl{ |
| "TestParcelable", "::android::Parcelable", type_params, std::move(publics), {}}}; |
| |
| vector<unique_ptr<Declaration>> classes; |
| classes.push_back(std::move(test)); |
| |
| unique_ptr<CppNamespace> test_ns{new CppNamespace{"test", std::move(classes)}}; |
| |
| vector<unique_ptr<Declaration>> test_ns_vec; |
| test_ns_vec.push_back(std::move(test_ns)); |
| |
| unique_ptr<CppNamespace> android_ns{new CppNamespace{"android", std::move(test_ns_vec)}}; |
| |
| vector<unique_ptr<Declaration>> 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<unique_ptr<AstNode>> 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<AstNode>(new Statement("foo"))); |
| block.AddStatement(unique_ptr<AstNode>(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<AstNode>{new Statement{"baz"}}); |
| auto case1 = s.AddCase("1"); |
| case1->AddStatement(unique_ptr<AstNode>{new Statement{"foo"}}); |
| case1->AddStatement(unique_ptr<AstNode>{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 |