blob: 9922917b66a8e541237e7b636340d5fd14a6d3a9 [file] [log] [blame]
Andrew de los Reyes35a7af12010-03-10 16:33:26 -08001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <set>
6#include <string>
7#include <utility>
8#include <vector>
9#include <gtest/gtest.h>
Chris Masone790e62e2010-08-12 10:41:18 -070010#include "base/logging.h"
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080011#include "update_engine/cycle_breaker.h"
12#include "update_engine/graph_types.h"
13#include "update_engine/utils.h"
14
15using std::make_pair;
16using std::pair;
17using std::set;
18using std::string;
19using std::vector;
20
21namespace chromeos_update_engine {
22
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070023namespace {
24void SetOpForNodes(Graph* graph) {
25 for (Graph::iterator it = graph->begin(), e = graph->end(); it != e; ++it) {
26 it->op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
27 }
28}
29} // namespace {}
30
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080031class CycleBreakerTest : public ::testing::Test {};
32
33TEST(CycleBreakerTest, SimpleTest) {
34 int counter = 0;
35 const Vertex::Index n_a = counter++;
36 const Vertex::Index n_b = counter++;
37 const Vertex::Index n_c = counter++;
38 const Vertex::Index n_d = counter++;
39 const Vertex::Index n_e = counter++;
40 const Vertex::Index n_f = counter++;
41 const Vertex::Index n_g = counter++;
42 const Vertex::Index n_h = counter++;
43 const Graph::size_type kNodeCount = counter++;
44
45 Graph graph(kNodeCount);
Andrew de los Reyes182a9f52010-10-05 16:33:51 -070046 SetOpForNodes(&graph);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080047
48 graph[n_a].out_edges.insert(make_pair(n_e, EdgeProperties()));
49 graph[n_a].out_edges.insert(make_pair(n_f, EdgeProperties()));
50 graph[n_b].out_edges.insert(make_pair(n_a, EdgeProperties()));
51 graph[n_c].out_edges.insert(make_pair(n_d, EdgeProperties()));
52 graph[n_d].out_edges.insert(make_pair(n_e, EdgeProperties()));
53 graph[n_d].out_edges.insert(make_pair(n_f, EdgeProperties()));
54 graph[n_e].out_edges.insert(make_pair(n_b, EdgeProperties()));
55 graph[n_e].out_edges.insert(make_pair(n_c, EdgeProperties()));
56 graph[n_e].out_edges.insert(make_pair(n_f, EdgeProperties()));
57 graph[n_f].out_edges.insert(make_pair(n_g, EdgeProperties()));
58 graph[n_g].out_edges.insert(make_pair(n_h, EdgeProperties()));
59 graph[n_h].out_edges.insert(make_pair(n_g, EdgeProperties()));
60
61 CycleBreaker breaker;
62
63 set<Edge> broken_edges;
64 breaker.BreakCycles(graph, &broken_edges);
65
66 // The following cycles must be cut:
67 // A->E->B
68 // C->D->E
69 // G->H
70
71 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_e)) ||
72 utils::SetContainsKey(broken_edges, make_pair(n_e, n_b)) ||
73 utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
74 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_c, n_d)) ||
75 utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)) ||
76 utils::SetContainsKey(broken_edges, make_pair(n_e, n_c)));
77 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_g, n_h)) ||
78 utils::SetContainsKey(broken_edges, make_pair(n_h, n_g)));
79 EXPECT_EQ(3, broken_edges.size());
80}
81
82namespace {
83pair<Vertex::Index, EdgeProperties> EdgeWithWeight(Vertex::Index dest,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070084uint64_t weight) {
Andrew de los Reyes35a7af12010-03-10 16:33:26 -080085 EdgeProperties props;
86 props.extents.resize(1);
87 props.extents[0].set_num_blocks(weight);
88 return make_pair(dest, props);
89}
90} // namespace {}
91
Andrew de los Reyes45109892010-07-26 13:49:31 -070092
93// This creates a bunch of cycles like this:
94//
95// root <------.
96// (t)-> / | \ |
97// V V V |
98// N N N |
99// \ | / |
100// VVV |
101// N |
102// / | \ |
103// V V V |
104// N N N |
105// ... |
106// (s)-> \ | / |
107// VVV |
108// N |
109// \_________/
110//
111// such that the original cutting algo would cut edges (s). We changed
112// the algorithm to cut cycles (t) instead, since they are closer to the
113// root, and that can massively speed up cycle cutting.
114TEST(CycleBreakerTest, AggressiveCutTest) {
115 int counter = 0;
116
117 const int kNodesPerGroup = 4;
118 const int kGroups = 33;
119
120 Graph graph(kGroups * kNodesPerGroup + 1); // + 1 for the root node
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700121 SetOpForNodes(&graph);
Andrew de los Reyes45109892010-07-26 13:49:31 -0700122
123 const Vertex::Index n_root = counter++;
124
125 Vertex::Index last_hub = n_root;
126 for (int i = 0; i < kGroups; i++) {
127 uint64_t weight = 5;
128 if (i == 0)
129 weight = 2;
130 else if (i == (kGroups - 1))
131 weight = 1;
132
133 const Vertex::Index next_hub = counter++;
134
135 for (int j = 0; j < (kNodesPerGroup - 1); j++) {
136 const Vertex::Index node = counter++;
137 graph[last_hub].out_edges.insert(EdgeWithWeight(node, weight));
138 graph[node].out_edges.insert(EdgeWithWeight(next_hub, weight));
139 }
140 last_hub = next_hub;
141 }
142
143 graph[last_hub].out_edges.insert(EdgeWithWeight(n_root, 5));
144
145
146 EXPECT_EQ(counter, graph.size());
147
148 CycleBreaker breaker;
149
150 set<Edge> broken_edges;
151 LOG(INFO) << "If this hangs for more than 1 second, the test has failed.";
152 breaker.BreakCycles(graph, &broken_edges);
153
154 set<Edge> expected_cuts;
155
156 for (Vertex::EdgeMap::const_iterator it = graph[n_root].out_edges.begin(),
157 e = graph[n_root].out_edges.end(); it != e; ++it) {
158 expected_cuts.insert(make_pair(n_root, it->first));
159 }
160
161 EXPECT_TRUE(broken_edges == expected_cuts);
162}
163
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800164TEST(CycleBreakerTest, WeightTest) {
165 int counter = 0;
166 const Vertex::Index n_a = counter++;
167 const Vertex::Index n_b = counter++;
168 const Vertex::Index n_c = counter++;
169 const Vertex::Index n_d = counter++;
170 const Vertex::Index n_e = counter++;
171 const Vertex::Index n_f = counter++;
172 const Vertex::Index n_g = counter++;
173 const Vertex::Index n_h = counter++;
174 const Vertex::Index n_i = counter++;
175 const Vertex::Index n_j = counter++;
176 const Graph::size_type kNodeCount = counter++;
177
178 Graph graph(kNodeCount);
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700179 SetOpForNodes(&graph);
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800180
181 graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 4));
182 graph[n_a].out_edges.insert(EdgeWithWeight(n_f, 3));
183 graph[n_a].out_edges.insert(EdgeWithWeight(n_h, 2));
184 graph[n_b].out_edges.insert(EdgeWithWeight(n_a, 3));
185 graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 4));
186 graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 5));
187 graph[n_c].out_edges.insert(EdgeWithWeight(n_d, 3));
188 graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 6));
189 graph[n_d].out_edges.insert(EdgeWithWeight(n_e, 3));
190 graph[n_e].out_edges.insert(EdgeWithWeight(n_d, 4));
191 graph[n_e].out_edges.insert(EdgeWithWeight(n_g, 5));
192 graph[n_f].out_edges.insert(EdgeWithWeight(n_g, 2));
193 graph[n_g].out_edges.insert(EdgeWithWeight(n_f, 3));
194 graph[n_g].out_edges.insert(EdgeWithWeight(n_d, 5));
195 graph[n_h].out_edges.insert(EdgeWithWeight(n_i, 8));
196 graph[n_i].out_edges.insert(EdgeWithWeight(n_e, 4));
197 graph[n_i].out_edges.insert(EdgeWithWeight(n_h, 9));
198 graph[n_i].out_edges.insert(EdgeWithWeight(n_j, 6));
199
200 CycleBreaker breaker;
201
202 set<Edge> broken_edges;
203 breaker.BreakCycles(graph, &broken_edges);
204
205 // These are required to be broken:
206 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
207 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_b, n_c)));
208 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)));
209 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_f, n_g)));
210 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_h, n_i)));
211}
212
Andrew de los Reyes8a51e5c2010-07-26 13:40:03 -0700213TEST(CycleBreakerTest, UnblockGraphTest) {
214 int counter = 0;
215 const Vertex::Index n_a = counter++;
216 const Vertex::Index n_b = counter++;
217 const Vertex::Index n_c = counter++;
218 const Vertex::Index n_d = counter++;
219 const Graph::size_type kNodeCount = counter++;
220
221 Graph graph(kNodeCount);
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700222 SetOpForNodes(&graph);
Andrew de los Reyes8a51e5c2010-07-26 13:40:03 -0700223
224 graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
225 graph[n_a].out_edges.insert(EdgeWithWeight(n_c, 1));
226 graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 2));
227 graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 2));
228 graph[n_b].out_edges.insert(EdgeWithWeight(n_d, 2));
229 graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 2));
230
231 CycleBreaker breaker;
232
233 set<Edge> broken_edges;
234 breaker.BreakCycles(graph, &broken_edges);
235
236 // These are required to be broken:
237 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_b)));
238 EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_c)));
239}
240
Andrew de los Reyes182a9f52010-10-05 16:33:51 -0700241TEST(CycleBreakerTest, SkipOpsTest) {
242 int counter = 0;
243 const Vertex::Index n_a = counter++;
244 const Vertex::Index n_b = counter++;
245 const Vertex::Index n_c = counter++;
246 const Graph::size_type kNodeCount = counter++;
247
248 Graph graph(kNodeCount);
249 SetOpForNodes(&graph);
250 graph[n_a].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
251 graph[n_c].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
252
253 graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
254 graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 1));
255
256 CycleBreaker breaker;
257
258 set<Edge> broken_edges;
259 breaker.BreakCycles(graph, &broken_edges);
260
261 EXPECT_EQ(2, breaker.skipped_ops());
262}
263
Andrew de los Reyes35a7af12010-03-10 16:33:26 -0800264} // namespace chromeos_update_engine