system_tests.cpp
1 // Copyright (c) 2019-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 // 5 6 #include <bitcoin-build-config.h> // IWYU pragma: keep 7 8 #include <common/run_command.h> 9 #include <test/util/common.h> 10 #include <test/util/setup_common.h> 11 #include <univalue.h> 12 #include <util/string.h> 13 14 #ifdef ENABLE_EXTERNAL_SIGNER 15 #include <util/subprocess.h> 16 #endif // ENABLE_EXTERNAL_SIGNER 17 18 #include <boost/cstdlib.hpp> 19 #include <boost/test/unit_test.hpp> 20 21 #include <string> 22 23 BOOST_FIXTURE_TEST_SUITE(system_tests, BasicTestingSetup) 24 25 #ifdef ENABLE_EXTERNAL_SIGNER 26 27 static std::vector<std::string> mock_executable(std::string name) 28 { 29 // Invoke the mock_process/* test case with all unsolicited output suppressed. 30 return { 31 boost::unit_test::framework::master_test_suite().argv[0], 32 // Disable false-positive memory leak dumps to stderr 33 // in debug builds when using the Windows UCRT. 34 "--detect_memory_leaks=0", 35 // Disable logging to stdout. 36 "--log_level=nothing", 37 // Disable the test report to stderr. 38 "--report_level=no", 39 "--run_test=mock_process/" + name, 40 }; 41 } 42 43 BOOST_AUTO_TEST_CASE(run_command) 44 { 45 { 46 const UniValue result = RunCommandParseJSON({}); 47 BOOST_CHECK(result.isNull()); 48 } 49 { 50 const UniValue result = RunCommandParseJSON(mock_executable("valid_json")); 51 BOOST_CHECK(result.isObject()); 52 const UniValue& success = result.find_value("success"); 53 BOOST_CHECK(!success.isNull()); 54 BOOST_CHECK_EQUAL(success.get_bool(), true); 55 } 56 { 57 // An invalid command is handled by cpp-subprocess 58 #ifdef WIN32 59 const std::string expected{"CreateProcess failed: "}; 60 #else 61 const std::string expected{"execve failed: "}; 62 #endif 63 BOOST_CHECK_EXCEPTION(RunCommandParseJSON({"invalid_command"}), subprocess::CalledProcessError, HasReason(expected)); 64 } 65 { 66 // Return non-zero exit code, no output to stderr 67 const std::vector<std::string> command = mock_executable("nonzeroexit_nooutput"); 68 BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, [&](const std::runtime_error& e) { 69 const std::string what{e.what()}; 70 BOOST_CHECK(what.find(strprintf("RunCommandParseJSON error: process(%s) returned %d: \n", util::Join(command, " "), boost::exit_test_failure)) != std::string::npos); 71 return true; 72 }); 73 } 74 { 75 // Return non-zero exit code, with error message for stderr 76 const std::vector<std::string> command = mock_executable("nonzeroexit_stderroutput"); 77 const std::string expected{"err"}; 78 BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, [&](const std::runtime_error& e) { 79 const std::string what(e.what()); 80 BOOST_CHECK(what.find(strprintf("RunCommandParseJSON error: process(%s) returned %s: %s", util::Join(command, " "), boost::exit_test_failure, "err")) != std::string::npos); 81 BOOST_CHECK(what.find(expected) != std::string::npos); 82 return true; 83 }); 84 } 85 { 86 // Unable to parse JSON 87 BOOST_CHECK_EXCEPTION(RunCommandParseJSON(mock_executable("invalid_json")), std::runtime_error, HasReason("Unable to parse JSON: {")); 88 } 89 { 90 // Test stdin 91 const UniValue result = RunCommandParseJSON(mock_executable("pass_stdin_to_stdout"), "{\"success\": true}"); 92 BOOST_CHECK(result.isObject()); 93 const UniValue& success = result.find_value("success"); 94 BOOST_CHECK(!success.isNull()); 95 BOOST_CHECK_EQUAL(success.get_bool(), true); 96 } 97 } 98 #endif // ENABLE_EXTERNAL_SIGNER 99 100 BOOST_AUTO_TEST_SUITE_END()