#include #include #include #include #include #include #include #include "file.h" namespace fs = std::filesystem; namespace bp = boost::process; namespace { const fs::path testpath{"testdir1"}; } class ymakeTest: public ::testing::Test { private: fs::path original_path; protected: ymakeTest() { } ~ymakeTest() override { } void SetUp() override { original_path = fs::current_path(); fs::remove_all(testpath); fs::create_directory(testpath); fs::current_path(testpath); } void TearDown() override { fs::current_path(original_path); fs::remove_all(testpath); } }; class yscanTest: public ymakeTest { }; void create_file(const fs::path& path, const std::string& contents) { Reichwein::File::setFile(path, contents); } boost::process::environment env_from_map(const std::unordered_map& map) { boost::process::environment result{boost::this_process::environment()}; for (const auto& pair: map) { result[pair.first] = pair.second; } return result; } int run_command(const std::string& command, const std::unordered_map& envmap = {}) { return bp::system(command, bp::std_out > bp::null, bp::std_err > bp::null, env_from_map(envmap)); } int run_command(const std::string& command, std::string& output, const std::unordered_map& envmap = {}) { bp::ipstream is; bp::ipstream es; int result = bp::system(command, bp::std_out > is, bp::std_err > es, env_from_map(envmap)); output = std::string(std::istreambuf_iterator(is), {}); output += std::string(std::istreambuf_iterator(es), {}); return result; } int run_command(const std::string& command, std::vector& output, const std::unordered_map& envmap = {}) { std::string output_string; int result = run_command(command, output_string, envmap); output = Reichwein::Stringhelper::split(output_string, "\r\n"); return result; } TEST_F(ymakeTest, build_one_cpp) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // compile, link result = run_command("./hello", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 0); // run } TEST_F(ymakeTest, build_one_cpp_compile_error) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { retrn 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); int result = run_command("../ymake"); EXPECT_NE(result, 0); } TEST_F(ymakeTest, clean_one_cpp) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake clean", output); EXPECT_EQ(output.size(), 0); ASSERT_EQ(result, 0); result = run_command("../ymake", output); EXPECT_EQ(output.size(), 2); // compile, link ASSERT_EQ(result, 0); result = run_command("../ymake clean", output); EXPECT_EQ(output.size(), 3); // rm .o, .d, executable ASSERT_EQ(result, 0); } TEST_F(ymakeTest, YMakefile_missing) { std::vector output; int result = run_command("../ymake", output); EXPECT_NE(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "ymake: YMakefile not found"); result = run_command("../ymake clean", output); EXPECT_NE(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "ymake: YMakefile not found"); } TEST_F(ymakeTest, build_three_cpp) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"( #include "second.h" )"); create_file("second.h", R"( #pragma once )"); create_file("third.cpp", R"( )"); create_file("YMakefile", R"( hello hello.cpp second.cpp third.cpp )"); EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("second.o")); EXPECT_TRUE(!fs::exists("third.o")); EXPECT_TRUE(!fs::exists("hello")); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 4); // compile 3, link 1 EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("second.o")); EXPECT_TRUE(fs::exists("third.o")); EXPECT_TRUE(fs::exists("hello")); result = run_command("../ymake clean"); EXPECT_EQ(result, 0); EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("second.o")); EXPECT_TRUE(!fs::exists("third.o")); EXPECT_TRUE(!fs::exists("hello")); } TEST_F(ymakeTest, rebuild) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"( #include "second.h" )"); create_file("second.h", R"( #pragma once )"); create_file("YMakefile", R"( hello hello.cpp second.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 3); // compile 2, link 1 run_command("touch hello.cpp"); result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // compile 1, link 1 } TEST_F(ymakeTest, build_cpp_c_cc) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.c", R"()"); create_file("third.cc", R"()"); create_file("YMakefile", R"( hello hello.cpp second.c third.cc )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 4); // 3 compile, 1 link } TEST_F(ymakeTest, multiple_builds) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("third.cpp", R"()"); create_file("YMakefile", R"( hello hello.cpp second second.cpp third.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 5); // 3 compile, 2 link } TEST_F(ymakeTest, build_and_test) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("third.cpp", R"()"); create_file("YMakefile", R"( hello hello.cpp second second.cpp third.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // 1 compile, 1 link result = run_command("../ymake test", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 4); // 2 compile, 1 link, 1 test result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 8); // hello second hello.o second.o thord.o hello.d second.d third.d result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 0); } TEST_F(ymakeTest, build_and_no_test_defined) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // 1 compile, 1 link result = run_command("../ymake test", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Everything up to date."); } TEST_F(ymakeTest, test_fail) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("test-hello.cpp", R"(int main(int argc, char* argv[]) { return 1; })"); create_file("YMakefile", R"( hello hello.cpp test-hello test-hello.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // 1 compile, 1 link result = run_command("../ymake test", output); EXPECT_EQ(result, 1); ASSERT_EQ(output.size(), 4); // 1 compile, 1 link, test: run, exit code } TEST_F(ymakeTest, test_only) { create_file("test-hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( test-hello test-hello.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Everything up to date."); result = run_command("../ymake test", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 3); // 1 compile, 1 link, test: run } TEST_F(ymakeTest, build_with_gcc) { if (!is_executable_command("g++")) { GTEST_SKIP() << "g++ not available"; } create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake", output, {{"CXX", "g++"}}); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 2); // compile, link for (const auto&i: output) { EXPECT_EQ(i.substr(0, 4), "g++ "); } } TEST_F(ymakeTest, build_with_clang) { if (!is_executable_command("clang++")) { GTEST_SKIP() << "clang++ not available"; } create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake", output, {{"CXX", "clang++"}}); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 2); // compile, link for (const auto&i: output) { EXPECT_EQ(i.substr(0, 8), "clang++ "); } } TEST_F(ymakeTest, multiple_dirs) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); fs::create_directory("subdir1"); create_file("subdir1/second.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("subdir1/YMakefile", R"( second second.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 4); // compile, link, compile, link EXPECT_TRUE(fs::exists("hello")); EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.d")); EXPECT_TRUE(fs::exists("subdir1/second")); EXPECT_TRUE(fs::exists("subdir1/second.o")); EXPECT_TRUE(fs::exists("subdir1/second.d")); result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 1); // everything up to date result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 6); // hello hello.o hello.d second second.o second.d result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 0); } TEST_F(ymakeTest, multiple_dirs_nested) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); fs::create_directory("subdir1"); create_file("subdir1/second.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("subdir1/YMakefile", R"( second second.cpp )"); fs::create_directory("subdir1/subdir2"); create_file("subdir1/subdir2/third.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("subdir1/subdir2/YMakefile", R"( third third.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 6); // 3x compile + link EXPECT_TRUE(fs::exists("hello")); EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.d")); EXPECT_TRUE(fs::exists("subdir1/second")); EXPECT_TRUE(fs::exists("subdir1/second.o")); EXPECT_TRUE(fs::exists("subdir1/second.d")); EXPECT_TRUE(fs::exists("subdir1/subdir2/third")); EXPECT_TRUE(fs::exists("subdir1/subdir2/third.o")); EXPECT_TRUE(fs::exists("subdir1/subdir2/third.d")); result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 1); // everything up to date result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 9); // hello hello.o hello.d second second.o second.d third.* result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 0); } TEST_F(ymakeTest, build_one_static_lib) { create_file("hello.cpp", R"(int maine(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"(int maini(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello.a hello.cpp second.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 3); // 2x compile, link EXPECT_EQ(output[2].substr(0, 7), "ar rcs "); EXPECT_TRUE(fs::exists("hello.d")); EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.a")); EXPECT_TRUE(fs::exists("second.d")); EXPECT_TRUE(fs::exists("second.o")); result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); // everything up to date result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 5); // deleted 5 files EXPECT_TRUE(!fs::exists("hello.d")); EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("hello.a")); EXPECT_TRUE(!fs::exists("second.d")); EXPECT_TRUE(!fs::exists("second.o")); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 0); } TEST_F(ymakeTest, build_one_dynamic_lib) { create_file("hello.cpp", R"(int maine(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"(int maini(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello.so.1.2.3 hello.cpp second.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 5); // 2x compile, link, 2 symlinks EXPECT_EQ(output[2].substr(0, 4), "g++ "); EXPECT_TRUE(fs::exists("hello.d")); EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.so.1.2.3")); EXPECT_TRUE(fs::exists("hello.so.1")); EXPECT_TRUE(fs::exists("hello.so")); EXPECT_TRUE(fs::exists("second.d")); EXPECT_TRUE(fs::exists("second.o")); result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); // everything up to date result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 7); // deleted 7 files EXPECT_TRUE(!fs::exists("hello.d")); EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("hello.so.1.2.3")); EXPECT_TRUE(!fs::exists("hello.so.1")); EXPECT_TRUE(!fs::exists("hello.so")); EXPECT_TRUE(!fs::exists("second.d")); EXPECT_TRUE(!fs::exists("second.o")); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 0); } TEST_F(ymakeTest, use_one_static_lib) { create_file("YMakefile", R"( hello.a hello.cpp runmain runmain.cpp hello.a )"); create_file("hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("hello.h", R"(#pragma once extern int hello(); )"); create_file("runmain.cpp", R"(#include "hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 4); // compile, link, compile, link EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.a")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // hello.o, hello.d, hello.a, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("hello.a")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_static_lib_from_subdir) { create_file("YMakefile", R"( runmain runmain.cpp subdir1/hello.a )"); create_file("runmain.cpp", R"(#include "subdir1/hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); fs::create_directory("subdir1"); create_file("subdir1/YMakefile", R"( hello.a hello.cpp )"); create_file("subdir1/hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("subdir1/hello.h", R"(#pragma once extern int hello(); )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 4); // compile, link, compile, link EXPECT_TRUE(fs::exists("subdir1/hello.o")); EXPECT_TRUE(fs::exists("subdir1/hello.a")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // hello.o, hello.d, hello.a, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("subdir1/hello.o")); EXPECT_TRUE(!fs::exists("subdir1/hello.a")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_static_lib_from_subsubdir) { create_file("YMakefile", R"( runmain runmain.cpp subdir1/subdir2/hello.a )"); create_file("runmain.cpp", R"(#include "subdir1/subdir2/hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); fs::create_directories("subdir1/subdir2"); create_file("subdir1/subdir2/YMakefile", R"( hello.a hello.cpp )"); create_file("subdir1/subdir2/hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("subdir1/subdir2/hello.h", R"(#pragma once extern int hello(); )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 4); // compile, link, compile, link EXPECT_TRUE(fs::exists("subdir1/subdir2/hello.o")); EXPECT_TRUE(fs::exists("subdir1/subdir2/hello.a")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // hello.o, hello.d, hello.a, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("subdir1/subdir2/hello.o")); EXPECT_TRUE(!fs::exists("subdir1/subdir2/hello.a")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_external_lib) { create_file("YMakefile", R"( runmain runmain.cpp fmt )"); create_file("runmain.cpp", R"( int main(int argc, char* argv[]) { return 0; })"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 2); // compile, link, compile, link EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 0); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 3); // runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_dynamic_lib) { create_file("YMakefile", R"( libhello.so.1.0.0 hello.cpp runmain runmain.cpp libhello.so )"); create_file("hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("hello.h", R"(#pragma once __attribute__((visibility("default"))) int hello(); )"); create_file("runmain.cpp", R"(#include "hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // compile, link, compile, link, 2 dyn. lib links EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("libhello.so.1.0.0")); EXPECT_TRUE(fs::exists("libhello.so.1")); EXPECT_TRUE(fs::exists("libhello.so")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 8); // hello.o, hello.d, hello.so.1.0.0, hello.so.1, hello.so, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("libhello.so.1.0.0")); EXPECT_TRUE(!fs::exists("libhello.so.1")); EXPECT_TRUE(!fs::exists("libhello.so")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_dynamic_lib_from_subdir) { create_file("YMakefile", R"( runmain runmain.cpp subdir1/libhello.so )"); create_file("runmain.cpp", R"(#include "subdir1/hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); fs::create_directory("subdir1"); create_file("subdir1/YMakefile", R"( libhello.so.1.0.0 hello.cpp )"); create_file("subdir1/hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("subdir1/hello.h", R"(#pragma once __attribute__((visibility("default"))) int hello(); )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // compile, link, compile, link, 2 dyn. lib links EXPECT_TRUE(fs::exists("subdir1/hello.o")); EXPECT_TRUE(fs::exists("subdir1/libhello.so.1.0.0")); EXPECT_TRUE(fs::exists("subdir1/libhello.so.1")); EXPECT_TRUE(fs::exists("subdir1/libhello.so")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 8); // hello.o, hello.d, hello.so.1.0.0, hello.so.1, hello.so, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("subdir1/hello.o")); EXPECT_TRUE(!fs::exists("subdir1/libhello.so.1.0.0")); EXPECT_TRUE(!fs::exists("subdir1/libhello.so.1")); EXPECT_TRUE(!fs::exists("subdir1/libhello.so")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, use_one_dynamic_lib_from_subsubdir) { create_file("YMakefile", R"( runmain runmain.cpp subdir1/subdir2/libhello.so )"); create_file("runmain.cpp", R"(#include "subdir1/subdir2/hello.h" int main(int argc, char* argv[]) { hello(); return 0; })"); fs::create_directories("subdir1/subdir2"); create_file("subdir1/subdir2/YMakefile", R"( libhello.so.1.0.0 hello.cpp )"); create_file("subdir1/subdir2/hello.cpp", R"(#include "hello.h" #include int hello() { std::cout << "Hello." << std::endl; return 0; })"); create_file("subdir1/subdir2/hello.h", R"(#pragma once __attribute__((visibility("default"))) int hello(); )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 6); // compile, link, compile, link, 2 dyn. lib links EXPECT_TRUE(fs::exists("subdir1/subdir2/hello.o")); EXPECT_TRUE(fs::exists("subdir1/subdir2/libhello.so.1.0.0")); EXPECT_TRUE(fs::exists("subdir1/subdir2/libhello.so.1")); EXPECT_TRUE(fs::exists("subdir1/subdir2/libhello.so")); EXPECT_TRUE(fs::exists("runmain.o")); EXPECT_TRUE(fs::exists("runmain")); result = run_command("./runmain", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 1); EXPECT_EQ(output[0], "Hello."); result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); ASSERT_EQ(output.size(), 8); // hello.o, hello.d, hello.so.1.0.0, hello.so.1, hello.so, runmain.o, runmain.d, runmain EXPECT_TRUE(!fs::exists("subdir1/subdir2/hello.o")); EXPECT_TRUE(!fs::exists("subdir1/subdir2/libhello.so.1.0.0")); EXPECT_TRUE(!fs::exists("subdir1/subdir2/libhello.so.1")); EXPECT_TRUE(!fs::exists("subdir1/subdir2/libhello.so")); EXPECT_TRUE(!fs::exists("runmain.o")); EXPECT_TRUE(!fs::exists("runmain")); } TEST_F(ymakeTest, dependencies_on_YMakefile) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("YMakefile", R"( hello hello.cpp )"); std::vector output; int result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // compile, link result = run_command("./hello", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 0); // run result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 1); // "Everything up to date" EXPECT_EQ(output[0], "Everything up to date."); result = run_command("touch YMakefile"); EXPECT_EQ(result, 0); result = run_command("../ymake", output); EXPECT_EQ(result, 0); EXPECT_EQ(output.size(), 2); // compile, link } TEST_F(yscanTest, no_cpp_file) { std::string output; int result = run_command("../yscan", output); EXPECT_EQ(result, 0); EXPECT_EQ(output, R"( testdir1 )"); } TEST_F(yscanTest, one_cpp_file) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); std::string output; int result = run_command("../yscan", output); EXPECT_EQ(result, 0); EXPECT_EQ(output, R"( testdir1 hello.cpp )"); } TEST_F(yscanTest, four_files) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("second.cpp", R"()"); create_file("third.cpp", R"()"); create_file("fourth.cpp", R"()"); std::string output; int result = run_command("../yscan", output); EXPECT_EQ(result, 0); EXPECT_EQ(output, R"( testdir1 fourth.cpp hello.cpp second.cpp third.cpp )"); } TEST_F(yscanTest, tests) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("test-second.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("test-third.cpp", R"()"); std::string output; int result = run_command("../yscan", output); EXPECT_EQ(result, 0); EXPECT_EQ(output, R"( testdir1 hello.cpp test-testdir1 test-second.cpp test-third.cpp )"); } TEST_F(yscanTest, unrelated_files) { create_file("hello.cpp", R"(int main(int argc, char* argv[]) { return 0; })"); create_file("Makefile", ""); create_file("other.h", ""); create_file("second.rb", ""); create_file("third.d", ""); std::string output; int result = run_command("../yscan", output); EXPECT_EQ(result, 0); EXPECT_EQ(output, R"( testdir1 hello.cpp )"); }