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.
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 @@
// provide a means to get available Edges in order and in a format which is
// easy to write tests around.
void FindWorkSorted(deque<Edge*>* 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 @@
/// 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 @@
virtual void Abort();
vector<string> commands_ran_;
- Edge* last_command_;
+ vector<Edge*> active_edges_;
+ size_t max_active_edges_;
VirtualFileSystem* fs_;
};
@@ -569,12 +570,13 @@
}
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 @@
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<Edge*>::iterator edge_iter = active_edges_.end() - 1;
+
+ Edge* edge = *edge_iter;
result->edge = edge;
if (edge->rule().name() == "interrupt" ||
@@ -619,7 +631,7 @@
result->status = ExitSuccess;
else
result->status = ExitFailure;
- last_command_ = NULL;
+ active_edges_.erase(edge_iter);
return true;
}
@@ -628,19 +640,33 @@
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<Edge*>::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<Edge*> FakeCommandRunner::GetActiveEdges() {
- vector<Edge*> 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) {