/*
 * Copyright 2019 Bloomberg Finance LP
 *
 * 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 <buildboxrun_commandfilemanager.h>

#include <buildboxcommon_fileutils.h>
#include <buildboxcommon_systemutils.h>

#include <gtest/gtest.h>
#include <string>
#include <unistd.h>
#include <vector>

using namespace buildboxcommon;
using namespace buildboxrun;
using namespace userchroot;

const std::string FILE_PREFIX = "command_file_test_";
const std::string LOCATION_IN_CHROOT = "/";

class CommandFileManagerTest : public ::testing::Test {

  protected:
    const std::string CURRENT_WORKING_DIR =
        SystemUtils::getCurrentWorkingDirectory();
};

TEST_F(CommandFileManagerTest, TestConstructor)
{
    Command command;
    *command.add_arguments() = "g++";
    *command.add_arguments() = "hello.cpp";
    command.set_working_directory("project");
    Command_EnvironmentVariable env;
    env.set_name("PATH");
    env.set_value("/test");
    *command.add_environment_variables() = env;
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ(
        "#!/bin/sh\nexport PATH='/test';\ncd '/project';\nrm $0;\nexec 'g++' "
        "'hello.cpp'",
        contents);
}

TEST_F(CommandFileManagerTest, TestConstructorMultipleEnvVars)
{
    Command command;

    std::vector<std::string> names{"PATH", "HOME", "PWD"};
    std::vector<std::string> values{"/test", "/home/test", "hi"};

    for (size_t i = 0; i < names.size(); ++i) {
        Command_EnvironmentVariable env;
        env.set_name(names[i]);
        env.set_value(values[i]);
        *command.add_environment_variables() = env;
    }

    std::string expected_string = "#!/bin/sh\n";
    for (size_t i = 0; i < names.size(); ++i) {
        expected_string += "export " + names[i] + "='" + values[i] + "';\n";
    }
    expected_string += "cd '/';\nrm $0;\nexec";

    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ(expected_string, contents);
}

TEST_F(CommandFileManagerTest, TestConstructorQuoting)
{
    Command command;
    command.set_working_directory("project's");
    *command.add_arguments() = "echo";
    *command.add_arguments() = "$jack's";
    Command_EnvironmentVariable env;
    env.set_name("PATH");
    env.set_value("test's '\"");
    *command.add_environment_variables() = env;
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ("#!/bin/sh\nexport PATH='test'\"'\"'s '\"'\"'\"';\ncd "
              "'/project'\"'\"'s';\nrm $0;\nexec 'echo' '$jack'\"'\"'s'",
              contents);
}

TEST_F(CommandFileManagerTest, TestConstructorNoEnvVars)
{
    Command command;
    *command.add_arguments() = "g++";
    *command.add_arguments() = "hello.cpp";
    command.set_working_directory("project");
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ("#!/bin/sh\ncd '/project';\nrm $0;\nexec 'g++' 'hello.cpp'",
              contents);
}

TEST_F(CommandFileManagerTest, TestConstructorNoEnvNoWorkingDir)
{
    Command command;
    *command.add_arguments() = "g++";
    *command.add_arguments() = "hello.cpp";
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ("#!/bin/sh\ncd '/';\nrm $0;\nexec 'g++' 'hello.cpp'", contents);
}

TEST_F(CommandFileManagerTest, TestConstructorNothing)
{
    Command command;
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string contents =
        FileUtils::getFileContents(command_file.getAbsoluteFilePath().c_str());
    EXPECT_EQ("#!/bin/sh\ncd '/';\nrm $0;\nexec", contents);
}

TEST_F(CommandFileManagerTest, TestConstructorPermissions)
{
    Command command;
    CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                    LOCATION_IN_CHROOT, FILE_PREFIX);
    std::string file_location = command_file.getAbsoluteFilePath();
    EXPECT_EQ(access(file_location.c_str(), F_OK | R_OK | X_OK), 0);
}

TEST_F(CommandFileManagerTest, TestDestructor)
{
    std::string file_location;
    {
        Command command;
        CommandFileManager command_file(command, CURRENT_WORKING_DIR,
                                        LOCATION_IN_CHROOT, FILE_PREFIX);
        file_location = command_file.getAbsoluteFilePath();
        ASSERT_EQ(access(file_location.c_str(), F_OK), 0);
    }
    ASSERT_EQ(access(file_location.c_str(), F_OK), -1);
    EXPECT_EQ(errno, ENOENT);
}
