/*
 * 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 <memory>
#include <string>
#include <vector>

#include <android-base/logging.h>
#include <gtest/gtest.h>

#include "aidl.h"
#include "options.h"
#include "tests/fake_io_delegate.h"
#include "tests/test_data.h"
#include "tests/test_util.h"

using android::aidl::test::CanonicalNameToPath;
using android::aidl::test::FakeIoDelegate;
using std::string;
using std::unique_ptr;
using std::vector;

namespace android {
namespace aidl {

class EndToEndTest : public ::testing::Test {
 protected:
  void SetUp() override {
  }

  void AddStubAidls(const char** parcelables, const char** interfaces,
                    const char* cpp_header=nullptr) {
    for ( ; *parcelables; ++parcelables) {
      io_delegate_.AddStubParcelable(
          *parcelables, (cpp_header) ? cpp_header : "");
    }
    for ( ; *interfaces; ++interfaces) {
      io_delegate_.AddStubInterface(*interfaces);
    }
  }

  void CheckFileContents(const string& rel_path,
                         const string& expected_content) {
    string actual_content;
    ASSERT_TRUE(io_delegate_.GetWrittenContents(rel_path, &actual_content))
        << "Expected aidl to write to " << rel_path << " but it did not.";

    if (actual_content == expected_content) {
      return;  // success!
    }

    test::PrintDiff(expected_content, actual_content);
    FAIL() << "Actual contents of " << rel_path
           << " did not match expected content";
  }

  FakeIoDelegate io_delegate_;
};

TEST_F(EndToEndTest, IExampleInterface) {
  using namespace ::android::aidl::test_data::example_interface;

  vector<string> args = {
    "aidl",
    "-b",
    "-I .",
    "-d an/arbitrary/path/to/dep.P",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(options.InputFiles().front(), kInterfaceDefinition);
  io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
                                     {"Subclass1", "Subclass2"});
  AddStubAidls(kImportedParcelables, kImportedInterfaces);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutput);
  CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}

TEST_F(EndToEndTest, IExampleInterface_WithTrace) {
  using namespace ::android::aidl::test_data::example_interface;

  vector<string> args = {
    "aidl",
    "-b",
    "-I .",
    "-t", //trace
    "-d an/arbitrary/path/to/dep.P",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(options.InputFiles().front(), kInterfaceDefinition);
  io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
                                     {"Subclass1", "Subclass2"});
  AddStubAidls(kImportedParcelables, kImportedInterfaces);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithTrace);
  CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}

TEST_F(EndToEndTest, IExampleInterface_WithTransactionNames) {
  using namespace ::android::aidl::test_data::example_interface;

  vector<string> args = {
    "aidl",
    "-b",
    "-I .",
    "--transaction_name", //trace
    "-d an/arbitrary/path/to/dep.P",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(options.InputFiles().front(), kInterfaceDefinition);
  io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
                                     {"Subclass1", "Subclass2"});
  AddStubAidls(kImportedParcelables, kImportedInterfaces);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithTransactionNames);
  CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}

TEST_F(EndToEndTest, IExampleInterface_Outlining) {
  using namespace ::android::aidl::test_data::example_interface;

  vector<string> args = {
    "aidl",
    "-b",
    "-I .",
    "-d an/arbitrary/path/to/dep.P",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);
  options.onTransact_outline_threshold_ = 4;
  options.onTransact_non_outline_count_ = 3;

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(options.InputFiles().front(), kInterfaceDefinitionOutlining);
  io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
                                     {"Subclass1", "Subclass2"});
  AddStubAidls(kImportedParcelables, kImportedInterfaces);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutputOutlining);
  CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}

TEST_F(EndToEndTest, IExampleInterface_WithVersion) {
  using namespace ::android::aidl::test_data::example_interface;

  vector<string> args = {
    "aidl",
    "-b",
    "-I .",
    "-d an/arbitrary/path/to/dep.P",
    "--version=10",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);
  options.onTransact_outline_threshold_ = 4;
  options.onTransact_non_outline_count_ = 3;

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(options.InputFiles().front(), kInterfaceDefinitionOutlining);
  io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
                                     {"Subclass1", "Subclass2"});
  AddStubAidls(kImportedParcelables, kImportedInterfaces);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersion);
  CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}


TEST_F(EndToEndTest, IPingResponderCpp) {
  using namespace ::android::aidl::test_data::ping_responder;

  vector<string> args = {
    "aidl-cpp",
    "-d deps.P",
    "-I .",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kGenHeaderDir,
    kCppOutputPath};
  Options options = Options::From(args);

  // Set up input paths.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);
  AddStubAidls(kImportedParcelables, kImportedInterfaces, kCppParcelableHeader);

  // Check that we parse and generate code correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kCppOutputPath, kExpectedCppOutput);
  CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutput);
  CheckFileContents(kGenClientHeaderPath, kExpectedBpHeaderOutput);
  CheckFileContents(kGenServerHeaderPath, kExpectedBnHeaderOutput);
  CheckFileContents(options.DependencyFile(), kExpectedCppDepsOutput);
}

TEST_F(EndToEndTest, IPingResponderCpp_WithVersion) {
  using namespace ::android::aidl::test_data::ping_responder;

  vector<string> args = {
    "aidl-cpp",
    "-d deps.P",
    "-I .",
    "--version=10",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kGenHeaderDir,
    kCppOutputPath};
  Options options = Options::From(args);

  // Set up input paths.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);
  AddStubAidls(kImportedParcelables, kImportedInterfaces, kCppParcelableHeader);

  // Check that we parse and generate code correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersion);
  CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersion);
  CheckFileContents(kGenClientHeaderPath, kExpectedBpHeaderOutputWithVersion);
  CheckFileContents(kGenServerHeaderPath, kExpectedBnHeaderOutputWithVersion);
  CheckFileContents(options.DependencyFile(), kExpectedCppDepsOutput);
}

TEST_F(EndToEndTest, StringConstantsInCpp) {
  using namespace ::android::aidl::test_data::string_constants;

  vector<string> args = {
    "aidl-cpp",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kGenHeaderDir,
    kCppOutputPath};
  Options options = Options::From(args);

  // Set up input paths.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);

  // Check that we parse and generate code correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kCppOutputPath, kExpectedCppOutput);
  CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutput);
}

TEST_F(EndToEndTest, StringConstantsInJava) {
  using namespace ::android::aidl::test_data::string_constants;

  vector<string> args = {
    "aidl",
    "-b",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutput);
}

TEST_F(EndToEndTest, StringConstantsInCpp_WithVersion) {
  using namespace ::android::aidl::test_data::string_constants;

  vector<string> args = {
    "aidl-cpp",
    "--version=10",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kGenHeaderDir,
    kCppOutputPath};
  Options options = Options::From(args);

  // Set up input paths.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);

  // Check that we parse and generate code correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersion);
  CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersion);
}

TEST_F(EndToEndTest, StringConstantsInJava_WithVersion) {
  using namespace ::android::aidl::test_data::string_constants;

  vector<string> args = {
    "aidl",
    "-b",
    "--version=10",
    CanonicalNameToPath(kCanonicalName, ".aidl"),
    kJavaOutputPath};
  Options options = Options::From(args);

  // Load up our fake file system with data.
  io_delegate_.SetFileContents(CanonicalNameToPath(kCanonicalName, ".aidl"), kInterfaceDefinition);

  // Check that we parse correctly.
  EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
  CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersion);
}

}  // namespace android
}  // namespace aidl