From 70d356218beff99ddaa048ff357f2d2692a32b7a Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 20 Jun 2017 11:51:13 -0400 Subject: Teach FakeCommandRunner to support multiple active commands Replace our single active edge pointer with a vector and add a parameter that tests can set to limit the number of concurrent edges. Set the default to 1 to preserve the current behavior. Specific tests will be able to override it later to simulate concurrent builds. --- src/build_test.cc | 68 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/build_test.cc b/src/build_test.cc index b50b66f..0ca7c3d 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -21,6 +21,12 @@ #include "graph.h" #include "test.h" +struct CompareEdgesByOutput { + static bool cmp(const Edge* a, const Edge* b) { + return a->outputs_[0]->path() < b->outputs_[0]->path(); + } +}; + /// Fixture for tests involving Plan. // Though Plan doesn't use State, it's useful to have one around // to create Nodes and Edges. @@ -31,12 +37,6 @@ struct PlanTest : public StateTestWithBuiltinRules { // provide a means to get available Edges in order and in a format which is // easy to write tests around. void FindWorkSorted(deque* ret, int count) { - struct CompareEdgesByOutput { - static bool cmp(const Edge* a, const Edge* b) { - return a->outputs_[0]->path() < b->outputs_[0]->path(); - } - }; - for (int i = 0; i < count; ++i) { ASSERT_TRUE(plan_.more_to_do()); Edge* edge = plan_.FindWork(); @@ -467,7 +467,7 @@ TEST_F(PlanTest, PoolWithFailingEdge) { /// Fake implementation of CommandRunner, useful for tests. struct FakeCommandRunner : public CommandRunner { explicit FakeCommandRunner(VirtualFileSystem* fs) : - last_command_(NULL), fs_(fs) {} + max_active_edges_(1), fs_(fs) {} // CommandRunner impl virtual bool CanRunMore(); @@ -477,7 +477,8 @@ struct FakeCommandRunner : public CommandRunner { virtual void Abort(); vector commands_ran_; - Edge* last_command_; + vector active_edges_; + size_t max_active_edges_; VirtualFileSystem* fs_; }; @@ -569,12 +570,13 @@ void BuildTest::RebuildTarget(const string& target, const char* manifest, } bool FakeCommandRunner::CanRunMore() { - // Only run one at a time. - return last_command_ == NULL; + return active_edges_.size() < max_active_edges_; } bool FakeCommandRunner::StartCommand(Edge* edge) { - assert(!last_command_); + assert(active_edges_.size() < max_active_edges_); + assert(find(active_edges_.begin(), active_edges_.end(), edge) + == active_edges_.end()); commands_ran_.push_back(edge->EvaluateCommand()); if (edge->rule().name() == "cat" || edge->rule().name() == "cat_rsp" || @@ -597,15 +599,25 @@ bool FakeCommandRunner::StartCommand(Edge* edge) { return false; } - last_command_ = edge; + active_edges_.push_back(edge); + + // Allow tests to control the order by the name of the first output. + sort(active_edges_.begin(), active_edges_.end(), + CompareEdgesByOutput::cmp); + return true; } bool FakeCommandRunner::WaitForCommand(Result* result) { - if (!last_command_) + if (active_edges_.empty()) return false; - Edge* edge = last_command_; + // All active edges were already completed immediately when started, + // so we can pick any edge here. Pick the last edge. Tests can + // control the order of edges by the name of the first output. + vector::iterator edge_iter = active_edges_.end() - 1; + + Edge* edge = *edge_iter; result->edge = edge; if (edge->rule().name() == "interrupt" || @@ -619,7 +631,7 @@ bool FakeCommandRunner::WaitForCommand(Result* result) { result->status = ExitSuccess; else result->status = ExitFailure; - last_command_ = NULL; + active_edges_.erase(edge_iter); return true; } @@ -628,19 +640,33 @@ bool FakeCommandRunner::WaitForCommand(Result* result) { result->status = ExitFailure; else result->status = ExitSuccess; - last_command_ = NULL; + + // Provide a way for test cases to verify when an edge finishes that + // some other edge is still active. This is useful for test cases + // covering behavior involving multiple active edges. + const string& verify_active_edge = edge->GetBinding("verify_active_edge"); + if (!verify_active_edge.empty()) { + bool verify_active_edge_found = false; + for (vector::iterator i = active_edges_.begin(); + i != active_edges_.end(); ++i) { + if ((*i)->outputs_.size() >= 1 && + (*i)->outputs_[0]->path() == verify_active_edge) { + verify_active_edge_found = true; + } + } + EXPECT_TRUE(verify_active_edge_found); + } + + active_edges_.erase(edge_iter); return true; } vector FakeCommandRunner::GetActiveEdges() { - vector edges; - if (last_command_) - edges.push_back(last_command_); - return edges; + return active_edges_; } void FakeCommandRunner::Abort() { - last_command_ = NULL; + active_edges_.clear(); } void BuildTest::Dirty(const string& path) { -- cgit v0.12