1//
2// Copyright (C) 2010 The Android Open Source Project
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#include "update_engine/payload_generator/cycle_breaker.h"
18
19#include <set>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include <base/logging.h>
25#include <gtest/gtest.h>
26
27#include "update_engine/common/utils.h"
28#include "update_engine/payload_generator/graph_types.h"
29
30using std::make_pair;
31using std::pair;
32using std::set;
33using std::string;
34using std::vector;
35
36namespace chromeos_update_engine {
37
38namespace {
39void SetOpForNodes(Graph* graph) {
40  for (Vertex& vertex : *graph) {
41    vertex.aop.op.set_type(InstallOperation::MOVE);
42  }
43}
44}  // namespace
45
46class CycleBreakerTest : public ::testing::Test {};
47
48TEST(CycleBreakerTest, SimpleTest) {
49  int counter = 0;
50  const Vertex::Index n_a = counter++;
51  const Vertex::Index n_b = counter++;
52  const Vertex::Index n_c = counter++;
53  const Vertex::Index n_d = counter++;
54  const Vertex::Index n_e = counter++;
55  const Vertex::Index n_f = counter++;
56  const Vertex::Index n_g = counter++;
57  const Vertex::Index n_h = counter++;
58  const Graph::size_type kNodeCount = counter++;
59
60  Graph graph(kNodeCount);
61  SetOpForNodes(&graph);
62
63  graph[n_a].out_edges.insert(make_pair(n_e, EdgeProperties()));
64  graph[n_a].out_edges.insert(make_pair(n_f, EdgeProperties()));
65  graph[n_b].out_edges.insert(make_pair(n_a, EdgeProperties()));
66  graph[n_c].out_edges.insert(make_pair(n_d, EdgeProperties()));
67  graph[n_d].out_edges.insert(make_pair(n_e, EdgeProperties()));
68  graph[n_d].out_edges.insert(make_pair(n_f, EdgeProperties()));
69  graph[n_e].out_edges.insert(make_pair(n_b, EdgeProperties()));
70  graph[n_e].out_edges.insert(make_pair(n_c, EdgeProperties()));
71  graph[n_e].out_edges.insert(make_pair(n_f, EdgeProperties()));
72  graph[n_f].out_edges.insert(make_pair(n_g, EdgeProperties()));
73  graph[n_g].out_edges.insert(make_pair(n_h, EdgeProperties()));
74  graph[n_h].out_edges.insert(make_pair(n_g, EdgeProperties()));
75
76  CycleBreaker breaker;
77
78  set<Edge> broken_edges;
79  breaker.BreakCycles(graph, &broken_edges);
80
81  // The following cycles must be cut:
82  // A->E->B
83  // C->D->E
84  // G->H
85
86  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_e)) ||
87              utils::SetContainsKey(broken_edges, make_pair(n_e, n_b)) ||
88              utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
89  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_c, n_d)) ||
90              utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)) ||
91              utils::SetContainsKey(broken_edges, make_pair(n_e, n_c)));
92  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_g, n_h)) ||
93              utils::SetContainsKey(broken_edges, make_pair(n_h, n_g)));
94  EXPECT_EQ(3U, broken_edges.size());
95}
96
97namespace {
98pair<Vertex::Index, EdgeProperties> EdgeWithWeight(Vertex::Index dest,
99uint64_t weight) {
100  EdgeProperties props;
101  props.extents.resize(1);
102  props.extents[0].set_num_blocks(weight);
103  return make_pair(dest, props);
104}
105}  // namespace
106
107
108// This creates a bunch of cycles like this:
109//
110//               root <------.
111//    (t)->     / | \        |
112//             V  V  V       |
113//             N  N  N       |
114//              \ | /        |
115//               VVV         |
116//                N          |
117//              / | \        |
118//             V  V  V       |
119//             N  N  N       |
120//               ...         |
121//     (s)->    \ | /        |
122//               VVV         |
123//                N          |
124//                 \_________/
125//
126// such that the original cutting algo would cut edges (s). We changed
127// the algorithm to cut cycles (t) instead, since they are closer to the
128// root, and that can massively speed up cycle cutting.
129TEST(CycleBreakerTest, AggressiveCutTest) {
130  size_t counter = 0;
131
132  const int kNodesPerGroup = 4;
133  const int kGroups = 33;
134
135  Graph graph(kGroups * kNodesPerGroup + 1);  // + 1 for the root node
136  SetOpForNodes(&graph);
137
138  const Vertex::Index n_root = counter++;
139
140  Vertex::Index last_hub = n_root;
141  for (int i = 0; i < kGroups; i++) {
142    uint64_t weight = 5;
143    if (i == 0)
144      weight = 2;
145    else if (i == (kGroups - 1))
146      weight = 1;
147
148    const Vertex::Index next_hub = counter++;
149
150    for (int j = 0; j < (kNodesPerGroup - 1); j++) {
151      const Vertex::Index node = counter++;
152      graph[last_hub].out_edges.insert(EdgeWithWeight(node, weight));
153      graph[node].out_edges.insert(EdgeWithWeight(next_hub, weight));
154    }
155    last_hub = next_hub;
156  }
157
158  graph[last_hub].out_edges.insert(EdgeWithWeight(n_root, 5));
159
160  EXPECT_EQ(counter, graph.size());
161
162  CycleBreaker breaker;
163
164  set<Edge> broken_edges;
165  LOG(INFO) << "If this hangs for more than 1 second, the test has failed.";
166  breaker.BreakCycles(graph, &broken_edges);
167
168  set<Edge> expected_cuts;
169
170  for (Vertex::EdgeMap::const_iterator it = graph[n_root].out_edges.begin(),
171       e = graph[n_root].out_edges.end(); it != e; ++it) {
172    expected_cuts.insert(make_pair(n_root, it->first));
173  }
174
175  EXPECT_TRUE(broken_edges == expected_cuts);
176}
177
178TEST(CycleBreakerTest, WeightTest) {
179  size_t counter = 0;
180  const Vertex::Index n_a = counter++;
181  const Vertex::Index n_b = counter++;
182  const Vertex::Index n_c = counter++;
183  const Vertex::Index n_d = counter++;
184  const Vertex::Index n_e = counter++;
185  const Vertex::Index n_f = counter++;
186  const Vertex::Index n_g = counter++;
187  const Vertex::Index n_h = counter++;
188  const Vertex::Index n_i = counter++;
189  const Vertex::Index n_j = counter++;
190  const Graph::size_type kNodeCount = counter++;
191
192  Graph graph(kNodeCount);
193  SetOpForNodes(&graph);
194
195  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 4));
196  graph[n_a].out_edges.insert(EdgeWithWeight(n_f, 3));
197  graph[n_a].out_edges.insert(EdgeWithWeight(n_h, 2));
198  graph[n_b].out_edges.insert(EdgeWithWeight(n_a, 3));
199  graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 4));
200  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 5));
201  graph[n_c].out_edges.insert(EdgeWithWeight(n_d, 3));
202  graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 6));
203  graph[n_d].out_edges.insert(EdgeWithWeight(n_e, 3));
204  graph[n_e].out_edges.insert(EdgeWithWeight(n_d, 4));
205  graph[n_e].out_edges.insert(EdgeWithWeight(n_g, 5));
206  graph[n_f].out_edges.insert(EdgeWithWeight(n_g, 2));
207  graph[n_g].out_edges.insert(EdgeWithWeight(n_f, 3));
208  graph[n_g].out_edges.insert(EdgeWithWeight(n_d, 5));
209  graph[n_h].out_edges.insert(EdgeWithWeight(n_i, 8));
210  graph[n_i].out_edges.insert(EdgeWithWeight(n_e, 4));
211  graph[n_i].out_edges.insert(EdgeWithWeight(n_h, 9));
212  graph[n_i].out_edges.insert(EdgeWithWeight(n_j, 6));
213
214  CycleBreaker breaker;
215
216  set<Edge> broken_edges;
217  breaker.BreakCycles(graph, &broken_edges);
218
219  // These are required to be broken:
220  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_b, n_a)));
221  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_b, n_c)));
222  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_d, n_e)));
223  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_f, n_g)));
224  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_h, n_i)));
225}
226
227TEST(CycleBreakerTest, UnblockGraphTest) {
228  size_t counter = 0;
229  const Vertex::Index n_a = counter++;
230  const Vertex::Index n_b = counter++;
231  const Vertex::Index n_c = counter++;
232  const Vertex::Index n_d = counter++;
233  const Graph::size_type kNodeCount = counter++;
234
235  Graph graph(kNodeCount);
236  SetOpForNodes(&graph);
237
238  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
239  graph[n_a].out_edges.insert(EdgeWithWeight(n_c, 1));
240  graph[n_b].out_edges.insert(EdgeWithWeight(n_c, 2));
241  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 2));
242  graph[n_b].out_edges.insert(EdgeWithWeight(n_d, 2));
243  graph[n_d].out_edges.insert(EdgeWithWeight(n_a, 2));
244
245  CycleBreaker breaker;
246
247  set<Edge> broken_edges;
248  breaker.BreakCycles(graph, &broken_edges);
249
250  // These are required to be broken:
251  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_b)));
252  EXPECT_TRUE(utils::SetContainsKey(broken_edges, make_pair(n_a, n_c)));
253}
254
255TEST(CycleBreakerTest, SkipOpsTest) {
256  size_t counter = 0;
257  const Vertex::Index n_a = counter++;
258  const Vertex::Index n_b = counter++;
259  const Vertex::Index n_c = counter++;
260  const Graph::size_type kNodeCount = counter++;
261
262  Graph graph(kNodeCount);
263  SetOpForNodes(&graph);
264  graph[n_a].aop.op.set_type(InstallOperation::REPLACE_BZ);
265  graph[n_c].aop.op.set_type(InstallOperation::REPLACE);
266
267  graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
268  graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 1));
269
270  CycleBreaker breaker;
271
272  set<Edge> broken_edges;
273  breaker.BreakCycles(graph, &broken_edges);
274
275  EXPECT_EQ(2U, breaker.skipped_ops());
276}
277
278}  // namespace chromeos_update_engine
279