+LogSwitcher::LogSwitcher (shared_ptr<Log> log)
+ : _old (dcpomatic_log)
+{
+ dcpomatic_log = log;
+}
+
+
+LogSwitcher::~LogSwitcher ()
+{
+ dcpomatic_log = _old;
+}
+
+std::ostream&
+dcp::operator<< (std::ostream& s, dcp::Size i)
+{
+ s << i.width << "x" << i.height;
+ return s;
+}
+
+std::ostream&
+dcp::operator<< (std::ostream& s, dcp::Standard t)
+{
+ switch (t) {
+ case Standard::INTEROP:
+ s << "interop";
+ break;
+ case Standard::SMPTE:
+ s << "smpte";
+ break;
+ }
+ return s;
+}
+
+std::ostream&
+operator<< (std::ostream&s, VideoFrameType f)
+{
+ s << video_frame_type_to_string(f);
+ return s;
+}
+
+
+void
+Cleanup::add (boost::filesystem::path path)
+{
+ _paths.push_back (path);
+}
+
+
+void
+Cleanup::run ()
+{
+ boost::system::error_code ec;
+ for (auto i: _paths) {
+ boost::filesystem::remove_all (i, ec);
+ }
+}
+
+
+void stage (string, boost::optional<boost::filesystem::path>) {}
+void progress (float) {}
+
+
+void
+verify_dcp(boost::filesystem::path dir, vector<dcp::VerificationNote::Code> ignore)
+{
+ auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, TestPaths::xsd());
+ bool ok = true;
+ for (auto i: notes) {
+ if (find(ignore.begin(), ignore.end(), i.code()) == ignore.end()) {
+ std::cout << "\t" << dcp::note_to_string(i) << "\n";
+ ok = false;
+ }
+ }
+ BOOST_CHECK(ok);
+}
+
+
+void
+#ifdef DCPOMATIC_LINUX
+make_and_verify_dcp(shared_ptr<Film> film, vector<dcp::VerificationNote::Code> ignore, bool dcp_inspect, bool clairmeta)
+#else
+make_and_verify_dcp(shared_ptr<Film> film, vector<dcp::VerificationNote::Code> ignore, bool, bool)
+#endif
+{
+ film->write_metadata ();
+ make_dcp (film, TranscodeJob::ChangedBehaviour::IGNORE);
+ BOOST_REQUIRE (!wait_for_jobs());
+ verify_dcp({film->dir(film->dcp_name())}, ignore);
+
+#ifdef DCPOMATIC_LINUX
+ auto test_tools_path_env = getenv("DCPOMATIC_TEST_TOOLS_PATH");
+ string test_tools_path;
+ if (test_tools_path_env) {
+ test_tools_path = test_tools_path_env;
+ }
+
+ auto old_path_env = getenv("PATH");
+ string old_path;
+ if (old_path_env) {
+ old_path = old_path_env;
+ }
+
+ dcp::ScopeGuard sg = [old_path]() { setenv("PATH", old_path.c_str(), 1); };
+ string new_path = old_path;
+ if (!new_path.empty()) {
+ new_path += ":";
+ }
+ new_path += test_tools_path;
+ setenv("PATH", new_path.c_str(), 1);
+
+ auto dcp_inspect_env = getenv("DCPOMATIC_DCP_INSPECT");
+ if (dcp_inspect && dcp_inspect_env) {
+ boost::filesystem::path dcp_inspect(dcp_inspect_env);
+ auto cmd = String::compose("%1 %2 > %3 2>&1", dcp_inspect, film->dir(film->dcp_name()), film->file("dcp_inspect.log"));
+ auto result = system(cmd.c_str());
+ BOOST_CHECK_EQUAL(WEXITSTATUS(result), 0);
+ }
+
+ if (clairmeta && getenv("DCPOMATIC_CLAIRMETA")) {
+ auto cmd = String::compose("python3 -m clairmeta.cli check -type dcp %1 > %2 2>&1", film->dir(film->dcp_name()), film->file("clairmeta.log"));
+ auto result = system(cmd.c_str());
+ BOOST_CHECK_EQUAL(WEXITSTATUS(result), 0);
+ }
+#endif
+}
+
+
+void
+check_int_close (int a, int b, int d)
+{
+ BOOST_CHECK_MESSAGE (std::abs(a - b) < d, a << " differs from " << b << " by more than " << d);
+}
+
+
+void
+check_int_close (std::pair<int, int> a, std::pair<int, int> b, int d)
+{
+ check_int_close (a.first, b.first, d);
+ check_int_close (a.second, b.second, d);
+}
+
+
+ConfigRestorer::~ConfigRestorer()
+{
+ setup_test_config();
+}
+
+
+boost::filesystem::path
+find_file (boost::filesystem::path dir, string filename_part)
+{
+ boost::optional<boost::filesystem::path> found;
+ for (auto i: boost::filesystem::directory_iterator(dir)) {
+ if (i.path().filename().string().find(filename_part) != string::npos) {
+ BOOST_REQUIRE_MESSAGE(!found, "File " << filename_part << " found more than once in " << dir);
+ found = i;
+ }
+ }
+ BOOST_REQUIRE_MESSAGE(found, "Could not find " << filename_part << " in " << dir);
+ return *found;
+}
+
+
+Editor::Editor(boost::filesystem::path path)
+ : _path(path)
+ , _content(dcp::file_to_string(path))
+{
+
+}
+
+Editor::~Editor()
+{
+ auto f = fopen(_path.string().c_str(), "w");
+ BOOST_REQUIRE(f);
+ fwrite(_content.c_str(), _content.length(), 1, f);
+ fclose(f);
+}
+
+void
+Editor::replace(string a, string b)
+{
+ auto old_content = _content;
+ boost::algorithm::replace_all(_content, a, b);
+ BOOST_REQUIRE(_content != old_content);