Move most of the create CLI into create_cli.cc to make it more testable.
authorCarl Hetherington <cth@carlh.net>
Wed, 19 Mar 2025 16:07:26 +0000 (17:07 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 19 Mar 2025 19:50:22 +0000 (20:50 +0100)
src/lib/create_cli.cc
src/lib/create_cli.h
src/tools/dcpomatic_create.cc
test/create_cli_test.cc

index de53600a046ae11483925153a46e752c996dd54d..c7a49ada99fdc6c8ffe4aa373aeeacbba7168196 100644 (file)
 */
 
 
+#include "audio_content.h"
 #include "compose.hpp"
 #include "config.h"
+#include "content_factory.h"
 #include "create_cli.h"
+#include "cross.h"
+#include "dcp_content.h"
 #include "dcp_content_type.h"
 #include "dcpomatic_log.h"
 #include "film.h"
+#include "image_content.h"
+#include "job_manager.h"
 #include "ratio.h"
 #include "variant.h"
+#include "video_content.h"
 #include <dcp/raw_convert.h>
 #include <fmt/format.h>
 #include <iostream>
 #include <string>
 
 
-using std::cout;
+using std::dynamic_pointer_cast;
+using std::function;
 using std::make_shared;
 using std::shared_ptr;
 using std::string;
@@ -386,7 +394,7 @@ CreateCLI::CreateCLI(int argc, char* argv[])
 
 
 shared_ptr<Film>
-CreateCLI::make_film() const
+CreateCLI::make_film(function<void (string)> error) const
 {
        auto film = std::make_shared<Film>(output_dir);
        dcpomatic_log = film->log();
@@ -436,6 +444,78 @@ CreateCLI::make_film() const
 
        film->set_audio_channels(_audio_channels);
 
+       auto jm = JobManager::instance();
+
+       for (auto cli_content: content) {
+               auto const can = dcp::filesystem::canonical(cli_content.path);
+               vector<shared_ptr<::Content>> film_content_list;
+
+               if (dcp::filesystem::exists(can / "ASSETMAP") || (dcp::filesystem::exists(can / "ASSETMAP.xml"))) {
+                       auto dcp = make_shared<DCPContent>(can);
+                       film_content_list.push_back(dcp);
+                       if (cli_content.kdm) {
+                               dcp->add_kdm(dcp::EncryptedKDM(dcp::file_to_string(*cli_content.kdm)));
+                       }
+                       if (cli_content.cpl) {
+                               dcp->set_cpl(*cli_content.cpl);
+                       }
+               } else {
+                       /* I guess it's not a DCP */
+                       film_content_list = content_factory(can);
+               }
+
+               for (auto film_content: film_content_list) {
+                       film->examine_and_add_content(film_content);
+               }
+
+               while (jm->work_to_do()) {
+                       dcpomatic_sleep_seconds(1);
+               }
+
+               while (signal_manager->ui_idle() > 0) {}
+
+               for (auto film_content: film_content_list) {
+                       if (film_content->video) {
+                               film_content->video->set_frame_type(cli_content.frame_type);
+                       }
+                       if (film_content->audio && cli_content.channel) {
+                               for (auto stream: film_content->audio->streams()) {
+                                       AudioMapping mapping(stream->channels(), film->audio_channels());
+                                       for (int channel = 0; channel < stream->channels(); ++channel) {
+                                               mapping.set(channel, *cli_content.channel, 1.0f);
+                                       }
+                                       stream->set_mapping(mapping);
+                               }
+                       }
+                       if (film_content->audio && cli_content.gain) {
+                               film_content->audio->set_gain(*cli_content.gain);
+                       }
+               }
+       }
+
+       if (dcp_frame_rate) {
+               film->set_video_frame_rate(*dcp_frame_rate);
+       }
+
+       for (auto i: film->content()) {
+               auto ic = dynamic_pointer_cast<ImageContent>(i);
+               if (ic && ic->still()) {
+                       ic->video->set_length(still_length.get_value_or(10) * 24);
+               }
+       }
+
+       if (jm->errors()) {
+               for (auto i: jm->get()) {
+                       if (i->finished_in_error()) {
+                               error(fmt::format("{}\n", i->error_summary()));
+                               if (!i->error_details().empty()) {
+                                       error(fmt::format("{}\n", i->error_details()));
+                               }
+                       }
+               }
+               return {};
+       }
+
        return film;
 }
 
index 18115b4ca73b269c4fe52f93cc8e208e331dfb11..0c4d97b6c7d2bc0bc06cf1b0fa43efef3034e65c 100644 (file)
@@ -55,7 +55,7 @@ public:
        boost::optional<std::string> error;
        std::vector<Content> content;
 
-       std::shared_ptr<Film> make_film() const;
+       std::shared_ptr<Film> make_film(std::function<void (std::string)> error) const;
 
 private:
        friend struct ::create_cli_test;
index e0d5b3973d4988f15b779c1f7c88a9163297ab53..44bed75a9595debe54cdbcf4358ff8d46fca40eb 100644 (file)
 
 */
 
-#include "lib/audio_content.h"
-#include "lib/config.h"
-#include "lib/content_factory.h"
+
 #include "lib/create_cli.h"
 #include "lib/cross.h"
-#include "lib/dcp_content.h"
-#include "lib/dcp_content_type.h"
 #include "lib/film.h"
-#include "lib/image_content.h"
-#include "lib/job.h"
-#include "lib/job_manager.h"
-#include "lib/ratio.h"
 #include "lib/signal_manager.h"
+#include "lib/state.h"
 #include "lib/util.h"
 #include "lib/version.h"
-#include "lib/version.h"
-#include "lib/video_content.h"
 #include <dcp/exceptions.h>
 #include <dcp/filesystem.h>
 #include <libxml++/libxml++.h>
@@ -91,79 +82,11 @@ main (int argc, char* argv[])
        }
 
        signal_manager = new SimpleSignalManager ();
-       auto jm = JobManager::instance ();
 
        try {
-               auto film = cc.make_film();
-
-               for (auto cli_content: cc.content) {
-                       auto const can = dcp::filesystem::canonical(cli_content.path);
-                       vector<shared_ptr<Content>> film_content_list;
-
-                       if (dcp::filesystem::exists(can / "ASSETMAP") || (dcp::filesystem::exists(can / "ASSETMAP.xml"))) {
-                               auto dcp = make_shared<DCPContent>(can);
-                               film_content_list.push_back (dcp);
-                               if (cli_content.kdm) {
-                                       dcp->add_kdm (dcp::EncryptedKDM(dcp::file_to_string(*cli_content.kdm)));
-                               }
-                               if (cli_content.cpl) {
-                                       dcp->set_cpl(*cli_content.cpl);
-                               }
-                       } else {
-                               /* I guess it's not a DCP */
-                               film_content_list = content_factory (can);
-                       }
-
-                       for (auto film_content: film_content_list) {
-                               film->examine_and_add_content (film_content);
-                       }
-
-                       while (jm->work_to_do ()) {
-                               dcpomatic_sleep_seconds (1);
-                       }
-
-                       while (signal_manager->ui_idle() > 0) {}
-
-                       for (auto film_content: film_content_list) {
-                               if (film_content->video) {
-                                       film_content->video->set_frame_type (cli_content.frame_type);
-                               }
-                               if (film_content->audio && cli_content.channel) {
-                                       for (auto stream: film_content->audio->streams()) {
-                                               AudioMapping mapping(stream->channels(), film->audio_channels());
-                                               for (int channel = 0; channel < stream->channels(); ++channel) {
-                                                       mapping.set(channel, *cli_content.channel, 1.0f);
-                                               }
-                                               stream->set_mapping (mapping);
-                                       }
-                               }
-                               if (film_content->audio && cli_content.gain) {
-                                       film_content->audio->set_gain (*cli_content.gain);
-                               }
-                       }
-               }
-
-               if (cc.dcp_frame_rate) {
-                       film->set_video_frame_rate (*cc.dcp_frame_rate);
-               }
-
-               for (auto i: film->content()) {
-                       auto ic = dynamic_pointer_cast<ImageContent> (i);
-                       if (ic && ic->still()) {
-                               ic->video->set_length(cc.still_length.get_value_or(10) * 24);
-                       }
-               }
-
-               if (jm->errors ()) {
-                       for (auto i: jm->get()) {
-                               if (i->finished_in_error()) {
-                                       cerr << i->error_summary() << "\n";
-                                       if (!i->error_details().empty()) {
-                                            cout << i->error_details() << "\n";
-                                       }
-                               }
-                       }
-                       exit (EXIT_FAILURE);
+               auto film = cc.make_film([](string s) { cerr << s; });
+               if (!film) {
+                       exit(EXIT_FAILURE);
                }
 
                if (cc.output_dir) {
index ac184203decf17c7f595ca13b64482cdfa22a1b6..9716fbef3a2535d8e4ce663263a28d23b29ae872 100644 (file)
@@ -60,6 +60,11 @@ run (string cmd)
 
 BOOST_AUTO_TEST_CASE (create_cli_test)
 {
+       string collected_error;
+       auto error = [&collected_error](string s) {
+               collected_error += s;
+       };
+
        CreateCLI cc = run ("dcpomatic2_create --version");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK (cc.version);
@@ -73,12 +78,14 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
 
        cc = run ("dcpomatic2_create -h");
        BOOST_REQUIRE (cc.error);
+       BOOST_CHECK(collected_error.empty());
 
        cc = run ("dcpomatic2_create x --name frobozz --template bar");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL(cc._name, "frobozz");
        BOOST_REQUIRE(cc._template_name);
        BOOST_CHECK_EQUAL(*cc._template_name, "bar");
+       BOOST_CHECK(collected_error.empty());
 
        cc = run ("dcpomatic2_create x --dcp-content-type FTR");
        BOOST_CHECK (!cc.error);
@@ -186,18 +193,19 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
        BOOST_CHECK_EQUAL(*cc._video_bit_rate, 120000000);
        BOOST_CHECK (!cc.error);
 
-       cc = run ("dcpomatic2_create --channel L fred.wav --channel R jim.wav sheila.wav");
+       cc = run ("dcpomatic2_create --channel L test/data/L.wav --channel R test/data/R.wav test/data/Lfe.wav");
        BOOST_REQUIRE_EQUAL (cc.content.size(), 3U);
-       BOOST_CHECK_EQUAL (cc.content[0].path, "fred.wav");
+       BOOST_CHECK_EQUAL (cc.content[0].path, "test/data/L.wav");
        BOOST_CHECK (cc.content[0].channel);
        BOOST_CHECK (*cc.content[0].channel == dcp::Channel::LEFT);
-       BOOST_CHECK_EQUAL (cc.content[1].path, "jim.wav");
+       BOOST_CHECK_EQUAL (cc.content[1].path, "test/data/R.wav");
        BOOST_CHECK (cc.content[1].channel);
        BOOST_CHECK (*cc.content[1].channel == dcp::Channel::RIGHT);
-       BOOST_CHECK_EQUAL (cc.content[2].path, "sheila.wav");
+       BOOST_CHECK_EQUAL (cc.content[2].path, "test/data/Lfe.wav");
        BOOST_CHECK (!cc.content[2].channel);
-       auto film = cc.make_film();
+       auto film = cc.make_film(out, error);
        BOOST_CHECK_EQUAL(film->audio_channels(), 6);
+       BOOST_CHECK(collected_error.empty());
 
        cc = run ("dcpomatic2_create --channel foo fred.wav");
        BOOST_REQUIRE (cc.error);
@@ -230,20 +238,23 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
        BOOST_REQUIRE(cc.error);
        BOOST_CHECK_EQUAL(*cc.error, "dcpomatic2_create: audio channel count must be even");
 
-       cc = run("dcpomatic2_create --channel L fred.wav --channel R jim.wav --channel C sheila.wav");
+       cc = run("dcpomatic2_create --channel L test/data/L.wav --channel R test/data/R.wav --channel C test/data/C.wav");
        BOOST_CHECK(!cc.error);
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK_EQUAL(film->audio_channels(), 6);
+       BOOST_CHECK(collected_error.empty());
 
-       cc = run("dcpomatic2_create --channel L fred.wav --channel R jim.wav --channel HI sheila.wav");
+       cc = run("dcpomatic2_create --channel L test/data/L.wav --channel R test/data/R.wav --channel HI test/data/sine_440.wav");
        BOOST_CHECK(!cc.error);
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK_EQUAL(film->audio_channels(), 8);
+       BOOST_CHECK(collected_error.empty());
 
-       cc = run("dcpomatic2_create --channel L fred.wav --channel R jim.wav --channel C sheila.wav --audio-channels 16");
+       cc = run("dcpomatic2_create --channel L test/data/L.wav --channel R test/data/R.wav --channel C test/data/C.wav --audio-channels 16");
        BOOST_CHECK(!cc.error);
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK_EQUAL(film->audio_channels(), 16);
+       BOOST_CHECK(collected_error.empty());
 }
 
 
@@ -251,63 +262,83 @@ BOOST_AUTO_TEST_CASE(create_cli_template_test)
 {
        ConfigRestorer cr("test/data");
 
+       string collected_error;
+       auto error = [&collected_error](string s) {
+               collected_error += s;
+       };
+
        auto cc = run("dcpomatic2_create test/data/flat_red.png");
-       auto film = cc.make_film();
+       auto film = cc.make_film(error);
        BOOST_CHECK(!film->three_d());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template 2d");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->three_d());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template 2d --threed");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->three_d());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template 3d");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->three_d());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template 3d --twod");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->three_d());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->encrypted());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template unencrypted");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->encrypted());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template unencrypted --encrypt");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->encrypted());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template encrypted");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->encrypted());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template encrypted --no-encrypt");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->encrypted());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->interop());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template interop");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->interop());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template interop --standard SMPTE");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->interop());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template smpte");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(!film->interop());
+       BOOST_CHECK(collected_error.empty());
 
        cc = run("dcpomatic2_create test/data/flat_red.png --template smpte --standard interop");
-       film = cc.make_film();
+       film = cc.make_film(error);
        BOOST_CHECK(film->interop());
+       BOOST_CHECK(collected_error.empty());
 }