Skip to content

Commit a3cbff2

Browse files
authored
Register Test Config (#161)
1 parent ca0e5c3 commit a3cbff2

File tree

7 files changed

+327
-37
lines changed

7 files changed

+327
-37
lines changed

buildcc/lib/args/include/args/register.h

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "target/generator.h"
2626
#include "target/target.h"
2727

28+
#include "args/register/test_info.h"
29+
2830
#include "taskflow/taskflow.hpp"
2931

3032
namespace buildcc {
@@ -104,10 +106,9 @@ class Register {
104106
*
105107
* Target is added as the `{executable}` argument
106108
*/
107-
void
108-
Test(const Args::ToolchainState &toolchain_state, const std::string &command,
109-
const base::Target &target,
110-
const std::unordered_map<const char *, std::string> &arguments = {});
109+
void Test(const Args::ToolchainState &toolchain_state,
110+
const std::string &command, const base::Target &target,
111+
const TestConfig &config = TestConfig());
111112

112113
/**
113114
* @brief Builds the targets that have been dynamically added through
@@ -127,19 +128,6 @@ class Register {
127128
const tf::Taskflow &GetTaskflow() const { return build_tf_; }
128129

129130
private:
130-
struct TestInfo {
131-
TestInfo(const base::Target &target, const std::string &command,
132-
const std::unordered_map<const char *, std::string> &arguments)
133-
: target_(target), command_(command), arguments_(arguments) {}
134-
135-
void TestRunner() const;
136-
137-
private:
138-
const base::Target &target_;
139-
std::string command_;
140-
std::unordered_map<const char *, std::string> arguments_;
141-
};
142-
143131
private:
144132
void Initialize();
145133

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2021 Niket Naidu. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef ARGS_REGISTER_TEST_INFO_H_
18+
#define ARGS_REGISTER_TEST_INFO_H_
19+
20+
#include "target/target.h"
21+
22+
#include <optional>
23+
#include <unordered_map>
24+
25+
namespace buildcc {
26+
27+
struct TestOutput {
28+
enum class Type {
29+
DefaultBehaviour, // Do not redirect to user or tests, default printed on
30+
// console
31+
TestPrintOnStderr, // Test only redirects stderr and prints
32+
TestPrintOnStdout, // Test only redirects stdout and prints
33+
TestPrintOnStderrAndStdout, // Test redirects both and prints
34+
UserRedirect, // Redirects to user
35+
};
36+
37+
TestOutput(Type output_type = Type::TestPrintOnStderrAndStdout,
38+
std::vector<std::string> *redirect_stdout = nullptr,
39+
std::vector<std::string> *redirect_stderr = nullptr)
40+
: type_(output_type), redirect_stdout_to_user_(redirect_stdout),
41+
redirect_stderr_to_user_(redirect_stderr) {}
42+
43+
Type GetType() const { return type_; }
44+
std::vector<std::string> *GetRedirectStdoutToUser() const {
45+
return redirect_stdout_to_user_;
46+
}
47+
std::vector<std::string> *GetRedirectStderrToUser() const {
48+
return redirect_stderr_to_user_;
49+
}
50+
51+
private:
52+
Type type_;
53+
std::vector<std::string> *redirect_stdout_to_user_;
54+
std::vector<std::string> *redirect_stderr_to_user_;
55+
};
56+
57+
struct TestConfig {
58+
public:
59+
TestConfig(
60+
const std::unordered_map<const char *, std::string> &arguments = {},
61+
const std::optional<fs::path> &working_directory = {},
62+
const TestOutput &output = TestOutput())
63+
: arguments_(arguments), working_directory_(working_directory),
64+
output_(output) {}
65+
66+
const std::unordered_map<const char *, std::string> &GetArguments() const {
67+
return arguments_;
68+
}
69+
const std::optional<fs::path> &GetWorkingDirectory() const {
70+
return working_directory_;
71+
}
72+
const TestOutput &GetTestOutput() const { return output_; }
73+
74+
private:
75+
std::unordered_map<const char *, std::string> arguments_;
76+
std::optional<fs::path> working_directory_{};
77+
TestOutput output_;
78+
};
79+
80+
struct TestInfo {
81+
TestInfo(const BaseTarget &target, const std::string &command,
82+
const TestConfig &config = TestConfig())
83+
: target_(target), command_(command), config_(config) {}
84+
85+
void TestRunner() const;
86+
87+
private:
88+
const BaseTarget &target_;
89+
std::string command_;
90+
TestConfig config_;
91+
};
92+
93+
} // namespace buildcc
94+
95+
#endif

buildcc/lib/args/src/register.cpp

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,9 @@ void Register::Dep(const base::BuilderInterface &target,
8787
target_iter->second.succeed(dep_iter->second);
8888
}
8989

90-
void Register::Test(
91-
const Args::ToolchainState &toolchain_state, const std::string &command,
92-
const base::Target &target,
93-
const std::unordered_map<const char *, std::string> &arguments) {
90+
void Register::Test(const Args::ToolchainState &toolchain_state,
91+
const std::string &command, const base::Target &target,
92+
const TestConfig &config) {
9493
if (!(toolchain_state.build && toolchain_state.test)) {
9594
return;
9695
}
@@ -101,7 +100,7 @@ void Register::Test(
101100
"Call Register::Build API on target before Register::Test API");
102101

103102
const bool added =
104-
tests_.emplace(target.GetUniqueId(), TestInfo(target, command, arguments))
103+
tests_.emplace(target.GetUniqueId(), TestInfo(target, command, config))
105104
.second;
106105
env::assert_fatal(
107106
added, fmt::format("Could not register test {}", target.GetName()));
@@ -127,28 +126,77 @@ void Register::Env() {
127126

128127
//
129128

130-
void Register::TestInfo::TestRunner() const {
129+
void TestInfo::TestRunner() const {
131130
env::log_info(__FUNCTION__,
132131
fmt::format("Testing \'{}\'", target_.GetUniqueId()));
133132
Command command;
134133
command.AddDefaultArguments({
135134
{"executable", fmt::format("{}", target_.GetTargetPath())},
136135
});
137-
const std::string test_command = command.Construct(command_, arguments_);
136+
const std::string test_command =
137+
command.Construct(command_, config_.GetArguments());
138+
139+
// TODO, Shift this to a function
140+
std::vector<std::string> test_redirect_stdout;
141+
std::vector<std::string> test_redirect_stderr;
142+
143+
std::vector<std::string> *redirect_stdout{nullptr};
144+
std::vector<std::string> *redirect_stderr{nullptr};
145+
switch (config_.GetTestOutput().GetType()) {
146+
case TestOutput::Type::DefaultBehaviour:
147+
(void)test_redirect_stdout;
148+
(void)test_redirect_stderr;
149+
break;
150+
case TestOutput::Type::TestPrintOnStderr:
151+
redirect_stderr = &test_redirect_stderr;
152+
(void)test_redirect_stdout;
153+
break;
154+
case TestOutput::Type::TestPrintOnStdout:
155+
redirect_stdout = &test_redirect_stdout;
156+
(void)test_redirect_stderr;
157+
break;
158+
case TestOutput::Type::TestPrintOnStderrAndStdout:
159+
redirect_stdout = &test_redirect_stdout;
160+
redirect_stderr = &test_redirect_stderr;
161+
break;
162+
case TestOutput::Type::UserRedirect:
163+
redirect_stdout = config_.GetTestOutput().GetRedirectStdoutToUser();
164+
redirect_stderr = config_.GetTestOutput().GetRedirectStderrToUser();
165+
(void)test_redirect_stdout;
166+
(void)test_redirect_stderr;
167+
break;
168+
default:
169+
(void)test_redirect_stdout;
170+
(void)test_redirect_stderr;
171+
env::assert_fatal<false>("Invalid TestOutput::Type");
172+
break;
173+
};
138174

139-
std::vector<std::string> stdout_data;
140-
std::vector<std::string> stderr_data;
141175
const bool success =
142-
Command::Execute(test_command, &stdout_data, &stderr_data);
143-
(void)success;
144-
145-
// TODO, Add options for test verboseness
146-
// For now just print it out
147-
env::log_info("", target_.GetUniqueId());
148-
std::for_each(stdout_data.cbegin(), stdout_data.cend(),
149-
[](const auto &str) { env::log_info("STDOUT", str); });
150-
std::for_each(stderr_data.cbegin(), stderr_data.cend(),
151-
[](const auto &str) { env::log_info("STDERR", str); });
176+
Command::Execute(test_command, config_.GetWorkingDirectory(),
177+
redirect_stdout, redirect_stderr);
178+
env::assert_fatal(success,
179+
fmt::format("Could not run {}", target_.GetUniqueId()));
180+
181+
// Print
182+
switch (config_.GetTestOutput().GetType()) {
183+
case TestOutput::Type::TestPrintOnStderr:
184+
env::log_info(fmt::format("STDERR: {}", target_.GetUniqueId()),
185+
internal::aggregate(*redirect_stderr));
186+
break;
187+
case TestOutput::Type::TestPrintOnStdout:
188+
env::log_info(fmt::format("STDOUT: {}", target_.GetUniqueId()),
189+
internal::aggregate(*redirect_stdout));
190+
break;
191+
case TestOutput::Type::TestPrintOnStderrAndStdout:
192+
env::log_info(fmt::format("STDOUT: {}", target_.GetUniqueId()),
193+
internal::aggregate(*redirect_stdout));
194+
env::log_info(fmt::format("STDERR: {}", target_.GetUniqueId()),
195+
internal::aggregate(*redirect_stderr));
196+
break;
197+
default:
198+
break;
199+
}
152200
}
153201

154202
} // namespace buildcc

buildcc/lib/args/test/test_register.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,141 @@ TEST(RegisterTestGroup, Register_Test) {
539539
mock().checkExpectations();
540540
}
541541

542+
TEST(RegisterTestGroup, Register_TestWithOutput) {
543+
// Arguments
544+
std::vector<const char *> av{
545+
"",
546+
"--config",
547+
"configs/basic_parse.toml",
548+
};
549+
int argc = av.size();
550+
551+
buildcc::Args args;
552+
buildcc::Args::ToolchainArg gcc_toolchain;
553+
buildcc::Args::ToolchainArg msvc_toolchain;
554+
args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain);
555+
args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain);
556+
args.Parse(argc, av.data());
557+
558+
STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root");
559+
STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build");
560+
CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace);
561+
CHECK_TRUE(args.Clean());
562+
563+
// Make dummy toolchain and target
564+
buildcc::env::init(fs::current_path(), fs::current_path());
565+
buildcc::base::Toolchain toolchain(buildcc::base::Toolchain::Id::Gcc, "", "",
566+
"", "", "", "");
567+
buildcc::base::Target target("dummyT", buildcc::base::TargetType::Executable,
568+
toolchain, "");
569+
buildcc::base::Target dependency(
570+
"depT", buildcc::base::TargetType::Executable, toolchain, "");
571+
572+
buildcc::Args::ToolchainState stateSuccess{true, true};
573+
574+
// TestOutput::Type::DefaultBehaviour
575+
{
576+
buildcc::Register reg(args);
577+
mock().expectNCalls(1, "BuildTask_dummyT");
578+
reg.Build(
579+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
580+
target);
581+
reg.Test(
582+
stateSuccess, "{executable}", target,
583+
buildcc::TestConfig(
584+
{}, {},
585+
buildcc::TestOutput(buildcc::TestOutput::Type::DefaultBehaviour)));
586+
587+
buildcc::m::CommandExpect_Execute(1, true);
588+
reg.RunTest();
589+
}
590+
591+
// TestOutput::Type::TestPrintOnStderr
592+
{
593+
buildcc::Register reg(args);
594+
mock().expectNCalls(1, "BuildTask_dummyT");
595+
reg.Build(
596+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
597+
target);
598+
reg.Test(
599+
stateSuccess, "{executable}", target,
600+
buildcc::TestConfig(
601+
{}, {},
602+
buildcc::TestOutput(buildcc::TestOutput::Type::TestPrintOnStderr)));
603+
604+
buildcc::m::CommandExpect_Execute(1, true);
605+
reg.RunTest();
606+
}
607+
608+
// TestOutput::Type::TestPrintOnStdout
609+
{
610+
buildcc::Register reg(args);
611+
mock().expectNCalls(1, "BuildTask_dummyT");
612+
reg.Build(
613+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
614+
target);
615+
reg.Test(
616+
stateSuccess, "{executable}", target,
617+
buildcc::TestConfig(
618+
{}, {},
619+
buildcc::TestOutput(buildcc::TestOutput::Type::TestPrintOnStdout)));
620+
621+
buildcc::m::CommandExpect_Execute(1, true);
622+
reg.RunTest();
623+
}
624+
625+
// TestOutput::Type::TestPrintOnStderrAndStdout
626+
{
627+
buildcc::Register reg(args);
628+
mock().expectNCalls(1, "BuildTask_dummyT");
629+
reg.Build(
630+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
631+
target);
632+
reg.Test(stateSuccess, "{executable}", target,
633+
buildcc::TestConfig(
634+
{}, {},
635+
buildcc::TestOutput(
636+
buildcc::TestOutput::Type::TestPrintOnStderrAndStdout)));
637+
638+
buildcc::m::CommandExpect_Execute(1, true);
639+
reg.RunTest();
640+
}
641+
642+
// TestOutput::Type::UserRedirect
643+
{
644+
buildcc::Register reg(args);
645+
mock().expectNCalls(1, "BuildTask_dummyT");
646+
reg.Build(
647+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
648+
target);
649+
reg.Test(stateSuccess, "{executable}", target,
650+
buildcc::TestConfig(
651+
{}, {},
652+
buildcc::TestOutput(buildcc::TestOutput::Type::UserRedirect,
653+
nullptr, nullptr)));
654+
655+
buildcc::m::CommandExpect_Execute(1, true);
656+
reg.RunTest();
657+
}
658+
659+
// TestOutput::Type::UserRedirect
660+
{
661+
buildcc::Register reg(args);
662+
mock().expectNCalls(1, "BuildTask_dummyT");
663+
reg.Build(
664+
stateSuccess, [](buildcc::base::Target &target) { (void)target; },
665+
target);
666+
reg.Test(
667+
stateSuccess, "{executable}", target,
668+
buildcc::TestConfig(
669+
{}, {}, buildcc::TestOutput(buildcc::TestOutput::Type(65535))));
670+
CHECK_THROWS(std::exception, reg.RunTest());
671+
}
672+
673+
buildcc::env::deinit();
674+
mock().checkExpectations();
675+
}
676+
542677
int main(int ac, char **av) {
543678
MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
544679
return CommandLineTestRunner::RunAllTests(ac, av);

0 commit comments

Comments
 (0)