summaryrefslogtreecommitdiff
path: root/test/suite
diff options
context:
space:
mode:
Diffstat (limited to 'test/suite')
-rw-r--r--test/suite/ctor_files/ctor.cc.generated87
-rw-r--r--test/suite/ctor_files/ctor.cc.generated286
-rw-r--r--test/suite/test.cc370
-rwxr-xr-xtest/suite/test.sh112
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