diff options
Diffstat (limited to 'test/suite')
| -rw-r--r-- | test/suite/ctor_files/ctor.cc.generated | 87 | ||||
| -rw-r--r-- | test/suite/ctor_files/ctor.cc.generated2 | 86 | ||||
| -rw-r--r-- | test/suite/test.cc | 370 | ||||
| -rwxr-xr-x | test/suite/test.sh | 112 |
4 files changed, 544 insertions, 111 deletions
diff --git a/test/suite/ctor_files/ctor.cc.generated b/test/suite/ctor_files/ctor.cc.generated new file mode 100644 index 0000000..5bc2940 --- /dev/null +++ b/test/suite/ctor_files/ctor.cc.generated @@ -0,0 +1,87 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include <ctor.h> +#include <filesystem> +#include <iostream> +#include <fstream> + +namespace +{ +ctor::build_configurations ctorConfigs(const ctor::settings& settings) +{ + return + { + { + .target = "world", + .sources = { + { "world.cc", ctor::source_type::generated }, + }, + }, + { + .target = "foo", + .sources = { + { "foo.cc", ctor::source_type::generated }, + }, + }, + { + .target = "this_is_unused", + .sources = { + {"hello.cc", ctor::output_file{"world.cc"}}, + {"hello.cc", ctor::output_file{"foo.cc"}}, + }, + .function = [](const std::string& input, + const std::string& output, + const ctor::build_configuration& config, + const ctor::settings& settings) + { + namespace fs = std::filesystem; + std::cout << "Input: " << input << '\n'; + std::cout << "Output: " << output << '\n'; + fs::copy_file(input, output, fs::copy_options::overwrite_existing); + return 0; + } + }, + + { + .target = "many_to_one", + .sources = { + {"many_to_one.cc", ctor::source_type::generated} + } + }, + { + .target = "many_to_one.cc", + .sources = { + {"foo.cc", ctor::source_type::generated}, + {"hello.cc"}, + }, + .function = [](const std::vector<std::string>& input, + const std::string& output, + const ctor::build_configuration& config, + const ctor::settings& settings) + { + std::cout << "Output: " << output << '\n'; + std::ofstream ofs(output); + bool comment{true}; + for(const auto& input_file : input) + { + std::cout << "Input: " << input_file << '\n'; + std::ifstream ifs(input_file); + std::string line; + while(std::getline(ifs, line)) + { + ofs << line << '\n'; + } + if(comment) ofs << "/*\n"; + comment = false; + } + ofs << "*/\n"; + return 0; + } + }, + + }; +} +} + +REG(ctorConfigs); diff --git a/test/suite/ctor_files/ctor.cc.generated2 b/test/suite/ctor_files/ctor.cc.generated2 new file mode 100644 index 0000000..c78489f --- /dev/null +++ b/test/suite/ctor_files/ctor.cc.generated2 @@ -0,0 +1,86 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include <ctor.h> +#include <filesystem> +#include <iostream> +#include <fstream> + +namespace +{ +ctor::build_configurations ctorConfigs(const ctor::settings& settings) +{ + return + { + { + .target = "world", + .sources = { + { "world.cc", ctor::source_type::generated }, + }, + }, + { + .target = "foo", + .sources = { + { "foo.cc", ctor::source_type::generated }, + }, + }, + { + .target = "this_is_unused", + .sources = { + {"hello.cc", ctor::output_file{"world.cc"}}, + {"hello.cc", ctor::output_file{"foo.cc"}}, + }, + .function = [](const std::string& input, + const std::string& output, + const ctor::build_configuration& config, + const ctor::settings& settings) + { + namespace fs = std::filesystem; + std::cout << "Input: " << input << '\n'; + std::cout << "Output: " << output << '\n'; + fs::copy_file(input, output, fs::copy_options::overwrite_existing); + return 0; + } + }, + + { + .target = "many_to_one", + .sources = { + {"many_to_one.cc", ctor::source_type::generated} + } + }, + { + .target = "many_to_one.cc", + .sources = { + {"hello.cc"}, + }, + .function = [](const std::vector<std::string>& input, + const std::string& output, + const ctor::build_configuration& config, + const ctor::settings& settings) + { + std::cout << "Output: " << output << '\n'; + std::ofstream ofs(output); + bool comment{true}; + for(const auto& input_file : input) + { + std::cout << "Input: " << input_file << '\n'; + std::ifstream ifs(input_file); + std::string line; + while(std::getline(ifs, line)) + { + ofs << line << '\n'; + } + if(comment) ofs << "/*\n"; + comment = false; + } + ofs << "*/\n"; + return 0; + } + }, + + }; +} +} + +REG(ctorConfigs); diff --git a/test/suite/test.cc b/test/suite/test.cc new file mode 100644 index 0000000..bb62d9d --- /dev/null +++ b/test/suite/test.cc @@ -0,0 +1,370 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "../../src/util.cc" +#include "../../src/pointerlist.cc" +#include "../../src/execute.cc" + +#include <iostream> +#include <string> +#include <filesystem> +#include <thread> +#include <chrono> +#include <sstream> + +using namespace std::chrono_literals; + +int fail(int value = 1, + const std::source_location location = std::source_location::current()) +{ + std::cout << "*** Failure at line " << location.line() << '\n'; + exit(value); +} + +const std::string ctor_exe{"./ctor"}; +const std::string obj_ext{".o"}; + +void run_ctor(const std::vector<std::string>& args, + const std::source_location location = std::source_location::current()) +{ + ctor::settings settings{.verbose = 2}; + auto ret = execute(settings, ctor_exe, args); + if(ret != 0) + { + fail(ret, location); + } +} + +void assert_not_exists(const std::string& path, + const std::source_location location = std::source_location::current()) +{ + if(!std::filesystem::exists(path)) + { + fail(1, location); + } +} + +void assert_exists(const std::string& path, + const std::source_location location = std::source_location::current()) +{ + if(std::filesystem::exists(path)) + { + fail(1, location); + } +} + +void copy_config(std::string cfg) +{ + std::cout << "** ctor_files/ctor.cc." + cfg + "\n"; + std::filesystem::copy("ctor_files/ctor.cc." + cfg, "ctor.cc", + std::filesystem::copy_options::overwrite_existing); + if(std::filesystem::exists(ctor_exe)) + { + auto ctor_exe_time = std::filesystem::last_write_time(ctor_exe); + std::filesystem::last_write_time("ctor.cc", ctor_exe_time + 1s); + } +} + +class Tracker +{ +public: + Tracker(const std::string& file_) : file(file_) + { + capture(); // capture initial value + } + + void capture() + { + changed(); + } + + bool changed() + { + auto tmp = readFile(file); + auto content_changed = tmp != content; + content = tmp; + return content_changed; + } + +private: + std::string file; + std::string content; +}; + +void assert_not_changed(Tracker& tracker, + const std::source_location location = std::source_location::current()) +{ + if(tracker.changed()) + { + fail(1, location); + } +} + +void assert_changed(Tracker& tracker, + const std::source_location location = std::source_location::current()) +{ + if(!tracker.changed()) + { + fail(1, location); + } +} + +int main() +{ + ctor::settings settings{}; + settings.verbose = 2; + auto paths = get_paths(); + + std::string CXX = "g++"; + get_env("CXX", CXX); + std::string CTORDIR = "../../build"; + get_env("CTORDIR", CTORDIR); + std::string BUILDDIR = "build"; + get_env("BUILDDIR", BUILDDIR); + std::string CXXFLAGS; + get_env("CXXFLAGS", CXXFLAGS); + std::string LDFLAGS; + get_env("LDFLAGS", LDFLAGS); + + // Wipe the board + std::filesystem::remove_all(BUILDDIR); + std::filesystem::remove("configuration.cc"); + std::filesystem::remove("config.h"); + std::filesystem::remove(ctor_exe); + + ////////////////////////////////////////////////////////////////////////////// + // bootstrap + { + auto cxx_prog = locate(CXX, paths); + + std::vector<std::string> args = + {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src", + "ctor.cc", "-o", ctor_exe}; + if(!CXXFLAGS.empty()) + { + auto tokens = argsplit(CXXFLAGS); + for(const auto& token : tokens) + { + args.push_back(token); + } + } + if(!LDFLAGS.empty()) + { + auto tokens = argsplit(LDFLAGS); + for(const auto& token : tokens) + { + args.push_back(token); + } + } + + // Compile bootstrap binary + copy_config("base"); + auto ret = execute(settings, cxx_prog, args); + if(ret != 0) + { + fail(ret); + } + } + ////////////////////////////////////////////////////////////////////////////// + // check if source file changes are tracked + { + // No build files should have been created yet + assert_exists(BUILDDIR); + + // capture ctor binary before configure is called + Tracker ctor_bin(ctor_exe); + run_ctor( + {"configure", "--ctor-includedir", "../../src", + "--ctor-libdir=" + CTORDIR, "--build-dir=" + BUILDDIR}); + + // ctor should be rebuilt at this point, so binary should have changed + assert_changed(ctor_bin); + + // configuration.cc should have been generated now + assert_not_exists("configuration.cc"); + assert_not_exists("config.h"); + + // Shouldn't compile anything yet - only configure + assert_exists(BUILDDIR + "/hello-hello_cc" + obj_ext); + + ctor_bin.capture(); + + // Run normally to build project + run_ctor({"-v"}); + + // Compiled object should now exist + assert_not_exists(BUILDDIR + "/hello-hello_cc" + obj_ext); + + // ctor should not have been rebuilt, so binary should be the same + assert_not_changed(ctor_bin); + + std::this_thread::sleep_for(1100ms); + + auto time = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + std::filesystem::last_write_time("hello.cc", time + 1s); + std::this_thread::sleep_for(1100ms); + + // Run normally to rebuild hello.cc + run_ctor({"-v"}); + + // Object file should have been recompiled + auto time2 = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + if(time == time2) + { + fail(); + } + } + + ////////////////////////////////////////////////////////////////////////////// + // check if flags change trigger rebuild + { + // Replace -DFOO with -DBAR in foo external.cxxflags + copy_config("bar"); + Tracker configuration_cc_bin("configuration.cc"); + Tracker ctor_bin(ctor_exe); + auto time = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + std::this_thread::sleep_for(1100ms); + + // Run normally to reconfigure, rebuild ctor and rebuild hello.cc + run_ctor({"-v"}); + + auto time2 = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + if(time == time2) + { + fail(); + } + + assert_changed(configuration_cc_bin); + assert_changed(ctor_bin); + } + + ////////////////////////////////////////////////////////////////////////////// + // check if included files in ctor.cc is tracked for changes + { + copy_config("multi"); + Tracker configuration_cc_bin("configuration.cc"); + Tracker ctor_bin(ctor_exe); + auto time = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + std::this_thread::sleep_for(1100ms); + + // Run normally to reconfigure, rebuild ctor and rebuild hello.cc + run_ctor({"-v"}); + + auto time2 = + std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext); + if(time == time2) + { + fail(); + } + + assert_changed(configuration_cc_bin); + assert_changed(ctor_bin); + + // now touching foobar.h, should retrigger re-configuration + time = std::filesystem::last_write_time(ctor_exe); + std::filesystem::last_write_time("foobar.h", time + 1s); + std::this_thread::sleep_for(1100ms); + + // Run normally to reconfigure, rebuild ctor and rebuild hello.cc + run_ctor({"-v"}); + + time2 = std::filesystem::last_write_time(ctor_exe); + if(time == time2) + { + fail(); + } + } + + ////////////////////////////////////////////////////////////////////////////// + // generated part1: one-to-one generation + { + copy_config("generated"); + std::filesystem::remove(BUILDDIR + "/world.cc"); + std::filesystem::remove(BUILDDIR + "/foo.cc"); + + Tracker configuration_cc_bin("configuration.cc"); + Tracker ctor_bin(ctor_exe); + std::this_thread::sleep_for(1100ms); + + // Run normally to reconfigure, rebuild ctor and build world.cc + run_ctor({"-v", "world"}); + + assert_changed(configuration_cc_bin); + assert_changed(ctor_bin); + + // foo.cc should not be generated at this point + assert_exists(BUILDDIR+"/foo.cc"); + + auto time_w = std::filesystem::last_write_time(BUILDDIR + "/world.cc"); + auto time_wo = + std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR+ + "/world-world_cc" + obj_ext); + std::this_thread::sleep_for(1100ms); + + // now touching hello.cc, should trigger regeneration of world.cc and + // rebuild + std::filesystem::last_write_time("hello.cc", time_w + 1s); + run_ctor({"-v", "world"}); + + auto time_w2 = std::filesystem::last_write_time(BUILDDIR + "/world.cc"); + auto time_wo2 = + std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR + + "/world-world_cc" + obj_ext); + if(time_w == time_w2) + { + fail(); + } + if(time_wo == time_wo2) + { + fail(); + } + } + + ////////////////////////////////////////////////////////////////////////////// + // generated part2: many-to-one generation + { + std::filesystem::remove(BUILDDIR + "/world.cc"); + std::filesystem::remove(BUILDDIR + "/foo.cc"); + std::filesystem::remove(BUILDDIR + "/many_to_one.cc"); + + run_ctor({"-v", "many_to_one"}); + + // world.cc should not be generated at this point + assert_exists(BUILDDIR+"/world.cc"); + + // foo.cc should have been generated at this point + assert_not_exists(BUILDDIR+"/foo.cc"); + + // many_to_one.cc should have been generated + assert_not_exists(BUILDDIR+"/many_to_one.cc"); + + auto time = std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc"); + std::this_thread::sleep_for(1100ms); + + Tracker ctor_bin(ctor_exe); + + // remove "foo.cc" from sources list, so it only contains hello.cc + copy_config("generated2"); + + // rebuild + run_ctor({"-v", "many_to_one"}); + + // Verify that the change resulted in a ctor rebuild + assert_changed(ctor_bin); + + // Verify that source-list change triggered a new build of many_to_one.cc + auto time2 = + std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc"); + if(time == time2) + { + fail(); + } + } + + return 0; +} diff --git a/test/suite/test.sh b/test/suite/test.sh index c112351..4638c0d 100755 --- a/test/suite/test.sh +++ b/test/suite/test.sh @@ -1,114 +1,4 @@ #!/bin/bash : ${CXX:=g++} -: ${CTORDIR:=../../build} -: ${BUILDDIR:=build} -CXX=$(which $CXX) - -function fail -{ - echo "*** Failure at line $1" - exit 1 -} - -function ctor -{ - echo "*** Running: ./ctor $*" - ./ctor $* -} - -# Wipe the board -rm -Rf ${BUILDDIR} -rm -f configuration.cc -rm -f ctor - -echo "** ctor_files/ctor.cc.base" -cp ctor_files/ctor.cc.base ctor.cc - -# Compile bootstrap binary -$CXX -pthread -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO} - -# No build files should have been created yet -[ -d ${BUILDDIR} ] && fail ${LINENO} - -# capture md5 sum of ctor binary before configure is called -MD5=`md5sum ctor` -ctor configure --ctor-includedir ../../src --ctor-libdir=${CTORDIR} --build-dir=${BUILDDIR} - -# ctor should be rebuilt at this point, so md5 sum should have changed -(echo $MD5 | md5sum --status -c) && fail ${LINENO} - -# configuration.cc should have been generated now -[ ! -f configuration.cc ] && fail ${LINENO} - -# Shouldn't compile anything yet - only configure -[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO} - -MD5=`md5sum ctor` - -# Run normally to build project -ctor -v - -# Compiled object should now exist -[ ! -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO} - -# ctor should not have been rebuilt, so md5 sum should be the same -(echo $MD5 | md5sum --status -c) || fail ${LINENO} - -MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` -touch hello.cc -sleep 1.1 - -# Run normally to rebuild hello.cc -ctor -v - -# Object file should have been recompiled -MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` -[[ $MOD1 == $MOD2 ]] && fail ${LINENO} - -# Replacve -DFOO with -DBAR in foo external.cxxflags -echo "** ctor_files/ctor.cc.bar" -cp ctor_files/ctor.cc.bar ctor.cc - -MD5C=`md5sum configuration.cc` -MD5=`md5sum ctor` -MOD1=`stat -c %Y build/hello-hello_cc.o` -sleep 1.1 - -# Run normally to reconfigure, rebuild ctor and rebuild hello.cc -ctor -v - -MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` -[[ $MOD1 == $MOD2 ]] && fail ${LINENO} -(echo $MD5C | md5sum --status -c) && fail ${LINENO} -(echo $MD5 | md5sum --status -c) && fail ${LINENO} - -echo "** ctor_files/ctor.cc.multi" -cp ctor_files/ctor.cc.multi ctor.cc - -MD5C=`md5sum configuration.cc` -MD5=`md5sum ctor` -MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` -sleep 1.1 - -# Run normally to reconfigure, rebuild ctor and rebuild hello.cc -ctor -v - -MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` -[[ $MOD1 == $MOD2 ]] && fail ${LINENO} -(echo $MD5C | md5sum --status -c) && fail ${LINENO} -(echo $MD5 | md5sum --status -c) && fail ${LINENO} - -# now touching foobar.h, should retrigger re-configuration -touch foobar.h - -MOD1=`stat -c %Y ctor` -sleep 1.1 - -# Run normally to reconfigure, rebuild ctor and rebuild hello.cc -ctor -v - -MOD2=`stat -c %Y ctor` -[[ $MOD1 == $MOD2 ]] && fail ${LINENO} - -exit 0 +$CXX $LDFLAGS $CXXFLAGS -std=c++20 -Wall test.cc -o test && ./test |
