// Copyright 2017 The Bazel Authors. All rights reserved.
//
// 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 <windows.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>

#include "src/main/cpp/util/strings.h"
#include "src/tools/launcher/util/launcher_util.h"
#include "gtest/gtest.h"

namespace bazel {
namespace launcher {

using std::getenv;
using std::ios;
using std::ofstream;
using std::string;
using std::wstring;

class LaunchUtilTest : public ::testing::Test {
 protected:
  LaunchUtilTest() {}

  virtual ~LaunchUtilTest() {}

  void SetUp() override {
    char* tmpdir = getenv("TEST_TMPDIR");
    if (tmpdir != NULL) {
      test_tmpdir = blaze_util::CstringToWstring(string(tmpdir));
    } else {
      tmpdir = getenv("TEMP");
      ASSERT_FALSE(tmpdir == NULL);
      test_tmpdir = blaze_util::CstringToWstring(string(tmpdir));
    }
  }

  void TearDown() override {}

  wstring GetTmpDir() { return this->test_tmpdir; }

  // Create an empty file at path
  static void CreateEmptyFile(const wstring& path) {
    ofstream file_stream(path.c_str(), ios::out | ios::binary);
    file_stream.put('\0');
  }

 private:
  wstring test_tmpdir;
};

TEST_F(LaunchUtilTest, GetBinaryPathWithoutExtensionTest) {
  ASSERT_EQ(L"foo", GetBinaryPathWithoutExtension(L"foo.exe"));
  ASSERT_EQ(L"foo.sh", GetBinaryPathWithoutExtension(L"foo.sh.exe"));
  ASSERT_EQ(L"foo.sh", GetBinaryPathWithoutExtension(L"foo.sh"));
}

TEST_F(LaunchUtilTest, GetBinaryPathWithExtensionTest) {
  ASSERT_EQ(L"foo.exe", GetBinaryPathWithExtension(L"foo"));
  ASSERT_EQ(L"foo.sh.exe", GetBinaryPathWithExtension(L"foo.sh.exe"));
  ASSERT_EQ(L"foo.sh.exe", GetBinaryPathWithExtension(L"foo.sh"));
}

TEST_F(LaunchUtilTest, GetEscapedArgumentTest) {
  ASSERT_EQ(L"foo", GetEscapedArgument(L"foo", true));
  ASSERT_EQ(L"\"foo bar\"", GetEscapedArgument(L"foo bar", true));
  ASSERT_EQ(L"\"\\\"foo bar\\\"\"", GetEscapedArgument(L"\"foo bar\"", true));
  ASSERT_EQ(L"foo\\\\bar", GetEscapedArgument(L"foo\\bar", true));
  ASSERT_EQ(L"foo\\\"bar", GetEscapedArgument(L"foo\"bar", true));
  ASSERT_EQ(L"C:\\\\foo\\\\bar\\\\",
            GetEscapedArgument(L"C:\\foo\\bar\\", true));
  ASSERT_EQ(L"\"C:\\\\foo foo\\\\bar\\\\\"",
            GetEscapedArgument(L"C:\\foo foo\\bar\\", true));

  ASSERT_EQ(L"foo\\bar", GetEscapedArgument(L"foo\\bar", false));
  ASSERT_EQ(L"C:\\foo\\bar\\", GetEscapedArgument(L"C:\\foo\\bar\\", false));
  ASSERT_EQ(L"\"C:\\foo foo\\bar\\\"",
            GetEscapedArgument(L"C:\\foo foo\\bar\\", false));
}

TEST_F(LaunchUtilTest, DoesFilePathExistTest) {
  wstring file1 = GetTmpDir() + L"/foo";
  wstring file2 = GetTmpDir() + L"/bar";
  CreateEmptyFile(file1);
  ASSERT_TRUE(DoesFilePathExist(file1.c_str()));
  ASSERT_FALSE(DoesFilePathExist(file2.c_str()));
}

TEST_F(LaunchUtilTest, DoesDirectoryPathExistTest) {
  wstring dir1 = GetTmpDir() + L"/dir1";
  wstring dir2 = GetTmpDir() + L"/dir2";
  CreateDirectoryW(dir1.c_str(), NULL);
  ASSERT_TRUE(DoesDirectoryPathExist(dir1.c_str()));
  ASSERT_FALSE(DoesDirectoryPathExist(dir2.c_str()));
}

TEST_F(LaunchUtilTest, SetAndGetEnvTest) {
  ASSERT_TRUE(SetEnv(L"foo", L"bar"));
  wstring value;
  ASSERT_TRUE(GetEnv(L"foo", &value));
  ASSERT_EQ(value, L"bar");
  SetEnv(L"FOO", L"");
  ASSERT_FALSE(GetEnv(L"FOO", &value));
}

TEST_F(LaunchUtilTest, NormalizePathTest) {
  wstring value;
  ASSERT_TRUE(NormalizePath(L"C:\\foo\\bar\\", &value));
  ASSERT_EQ(L"c:\\foo\\bar", value);
  ASSERT_TRUE(NormalizePath(L"c:/foo/bar/", &value));
  ASSERT_EQ(L"c:\\foo\\bar", value);
  ASSERT_TRUE(NormalizePath(L"FoO\\\\bAr\\", &value));
  ASSERT_EQ(L"foo\\bar", value);
  ASSERT_TRUE(NormalizePath(L"X\\Y/Z\\", &value));
  ASSERT_EQ(L"x\\y\\z", value);
  ASSERT_TRUE(NormalizePath(L"c://foo//bar", &value));
  ASSERT_EQ(L"c:\\foo\\bar", value);
  ASSERT_FALSE(NormalizePath(L"c:foo\\bar", &value));
}

TEST_F(LaunchUtilTest, RelativeToTest) {
  wstring value;
  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar1", L"c:\\foo\\bar2", &value));
  ASSERT_EQ(L"..\\bar1", value);
  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\", &value));
  ASSERT_EQ(L"foo\\bar", value);
  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo\\bar", &value));
  ASSERT_EQ(L"", value);
  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo", &value));
  ASSERT_EQ(L"bar", value);
  ASSERT_TRUE(RelativeTo(L"c:\\foo\\bar", L"c:\\foo\\ba", &value));
  ASSERT_EQ(L"..\\bar", value);
  ASSERT_TRUE(RelativeTo(L"c:\\", L"c:\\foo", &value));
  ASSERT_EQ(L"..\\", value);
  ASSERT_TRUE(RelativeTo(L"c:\\", L"c:\\a\\b\\c", &value));
  ASSERT_EQ(L"..\\..\\..\\", value);
  ASSERT_TRUE(RelativeTo(L"c:\\aa\\bb\\cc", L"c:\\a\\b", &value));
  ASSERT_EQ(L"..\\..\\aa\\bb\\cc", value);

  ASSERT_TRUE(RelativeTo(L"foo\\bar", L"foo\\bar", &value));
  ASSERT_EQ(L"", value);
  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo\\bar2", &value));
  ASSERT_EQ(L"..\\bar1", value);
  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo\\bar", &value));
  ASSERT_EQ(L"..\\bar1", value);
  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"foo", &value));
  ASSERT_EQ(L"bar1", value);
  ASSERT_TRUE(RelativeTo(L"foo\\bar1", L"fo", &value));
  ASSERT_EQ(L"..\\foo\\bar1", value);
  ASSERT_TRUE(RelativeTo(L"foo\\ba", L"foo\\bar", &value));
  ASSERT_EQ(L"..\\ba", value);
  ASSERT_TRUE(RelativeTo(L"foo", L"foo\\bar", &value));
  ASSERT_EQ(L"..\\", value);
  ASSERT_TRUE(RelativeTo(L"fo", L"foo\\bar", &value));
  ASSERT_EQ(L"..\\..\\fo", value);
  ASSERT_TRUE(RelativeTo(L"", L"foo\\bar", &value));
  ASSERT_EQ(L"..\\..\\", value);
  ASSERT_TRUE(RelativeTo(L"foo\\bar", L"", &value));
  ASSERT_EQ(L"foo\\bar", value);
  ASSERT_TRUE(RelativeTo(L"a\\b\\c", L"x\\y", &value));
  ASSERT_EQ(L"..\\..\\a\\b\\c", value);

  ASSERT_FALSE(RelativeTo(L"c:\\foo\\bar1", L"foo\\bar2", &value));
  ASSERT_FALSE(RelativeTo(L"c:foo\\bar1", L"c:\\foo\\bar2", &value));
  ASSERT_FALSE(RelativeTo(L"c:\\foo\\bar1", L"d:\\foo\\bar2", &value));
}

}  // namespace launcher
}  // namespace bazel
