blob: 9b8436cc711c0f243131889163d3795b03425f96 [file] [log] [blame]
rspangler@google.com49fdf182009-10-10 00:57:34 +00001// Copyright (c) 2009 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 <string>
6#include <glib.h>
7#include <gtest/gtest.h>
8#include "update_engine/action_pipe.h"
9#include "update_engine/update_check_action.h"
10#include "update_engine/mock_http_fetcher.h"
11#include "update_engine/omaha_hash_calculator.h"
12#include "update_engine/test_utils.h"
13
14namespace chromeos_update_engine {
15
16using std::string;
17
18class UpdateCheckActionTest : public ::testing::Test { };
19
20namespace {
21string GetNoUpdateResponse(const string& app_id) {
22 return string(
23 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
24 "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
25 "appid=\"") + app_id + "\" status=\"ok\"><ping "
26 "status=\"ok\"/><updatecheck status=\"noupdate\"/></app></gupdate>";
27}
28
29string GetUpdateResponse(const string& app_id,
30 const string& display_version,
31 const string& more_info_url,
32 const string& prompt,
33 const string& codebase,
34 const string& hash,
35 const string& needsadmin,
36 const string& size) {
37 return string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
38 "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
39 "appid=\"") + app_id + "\" status=\"ok\"><ping "
40 "status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
41 "MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
42 "codebase=\"" + codebase + "\" "
43 "hash=\"" + hash + "\" needsadmin=\"" + needsadmin + "\" "
44 "size=\"" + size + "\" status=\"ok\"/></app></gupdate>";
45}
46
47class UpdateCheckActionTestProcessorDelegate : public ActionProcessorDelegate {
48 public:
49 UpdateCheckActionTestProcessorDelegate()
50 : loop_(NULL),
51 expected_success_(true) {}
52 virtual ~UpdateCheckActionTestProcessorDelegate() {
53 }
54 virtual void ProcessingDone(const ActionProcessor* processor) {
55 ASSERT_TRUE(loop_);
56 g_main_loop_quit(loop_);
57 }
58
59 virtual void ActionCompleted(const ActionProcessor* processor,
60 const AbstractAction* action,
61 bool success) {
62 // make sure actions always succeed
63 if (action->Type() == "UpdateCheckAction")
64 EXPECT_EQ(expected_success_, success);
65 else
66 EXPECT_TRUE(success);
67 }
68 GMainLoop *loop_;
69 bool expected_success_;
70};
71
72gboolean StartProcessorInRunLoop(gpointer data) {
73 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
74 processor->StartProcessing();
75 return FALSE;
76}
77
78} // namespace {}
79
80class OutputObjectCollectorAction;
81
82template<>
83class ActionTraits<OutputObjectCollectorAction> {
84 public:
85 // Does not take an object for input
86 typedef UpdateCheckResponse InputObjectType;
87 // On success, puts the output path on output
88 typedef NoneType OutputObjectType;
89};
90
91class OutputObjectCollectorAction : public Action<OutputObjectCollectorAction> {
92 public:
93 void PerformAction() {
94 // copy input object
95 has_input_object_ = HasInputObject();
96 if (has_input_object_)
97 update_check_response_ = GetInputObject();
98 processor_->ActionComplete(this, true);
99 }
100 // Should never be called
101 void TerminateProcessing() {
102 CHECK(false);
103 }
104 // Debugging/logging
105 std::string Type() const { return "OutputObjectCollectorAction"; }
106 bool has_input_object_;
107 UpdateCheckResponse update_check_response_;
108};
109
110// returns true iff an output response was obtained from the
111// UpdateCheckAction. out_response may be NULL.
112// out_post_data may be null; if non-null, the post-data received by the
113// mock HttpFetcher is returned.
114bool TestUpdateCheckAction(const UpdateCheckParams& params,
115 const string& http_response,
116 bool expected_success,
117 UpdateCheckResponse* out_response,
118 vector<char> *out_post_data) {
119 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
120 MockHttpFetcher *fetcher = new MockHttpFetcher(http_response.data(),
121 http_response.size());
122
123 UpdateCheckAction action(params, fetcher); // takes ownership of fetcher
124 UpdateCheckActionTestProcessorDelegate delegate;
125 delegate.loop_ = loop;
126 delegate.expected_success_ = expected_success;
127 ActionProcessor processor;
128 processor.set_delegate(&delegate);
129 processor.EnqueueAction(&action);
130
131 OutputObjectCollectorAction collector_action;
132
133 BondActions(&action, &collector_action);
134 processor.EnqueueAction(&collector_action);
135
136 g_timeout_add(0, &StartProcessorInRunLoop, &processor);
137 g_main_loop_run(loop);
138 g_main_loop_unref(loop);
139 if (collector_action.has_input_object_ && out_response)
140 *out_response = collector_action.update_check_response_;
141 if (out_post_data)
142 *out_post_data = fetcher->post_data();
143 return collector_action.has_input_object_;
144}
145
146TEST(UpdateCheckActionTest, NoUpdateTest) {
147 UpdateCheckParams params("", // machine_id
148 "", // user_id
149 UpdateCheckParams::kOsPlatform,
150 UpdateCheckParams::kOsVersion,
151 "", // os_sp
152 UpdateCheckParams::kAppId,
153 "0.1.0.0",
154 "en-US",
155 "unittest");
156 UpdateCheckResponse response;
157 ASSERT_TRUE(
158 TestUpdateCheckAction(params,
159 GetNoUpdateResponse(UpdateCheckParams::kAppId),
160 true,
161 &response,
162 NULL));
163 EXPECT_FALSE(response.update_exists);
164}
165
166TEST(UpdateCheckActionTest, ValidUpdateTest) {
167 UpdateCheckParams params("machine_id",
168 "user_id",
169 UpdateCheckParams::kOsPlatform,
170 UpdateCheckParams::kOsVersion,
171 "service_pack",
172 UpdateCheckParams::kAppId,
173 "0.1.0.0",
174 "en-US",
175 "unittest_track");
176 UpdateCheckResponse response;
177 ASSERT_TRUE(
178 TestUpdateCheckAction(params,
179 GetUpdateResponse(UpdateCheckParams::kAppId,
180 "1.2.3.4", // version
181 "http://more/info",
182 "true", // prompt
183 "http://code/base", // dl url
184 "HASH1234=", // checksum
185 "false", // needs admin
186 "123"), // size
187 true,
188 &response,
189 NULL));
190 EXPECT_TRUE(response.update_exists);
191 EXPECT_EQ("1.2.3.4", response.display_version);
192 EXPECT_EQ("http://code/base", response.codebase);
193 EXPECT_EQ("http://more/info", response.more_info_url);
194 EXPECT_EQ("HASH1234=", response.hash);
195 EXPECT_EQ(123, response.size);
196 EXPECT_FALSE(response.needs_admin);
197 EXPECT_TRUE(response.prompt);
198}
199
200TEST(UpdateCheckActionTest, NoOutputPipeTest) {
201 UpdateCheckParams params("", // machine_id
202 "", // usr_id
203 UpdateCheckParams::kOsPlatform,
204 UpdateCheckParams::kOsVersion,
205 "", // os_sp
206 UpdateCheckParams::kAppId,
207 "0.1.0.0",
208 "en-US",
209 "unittest");
210 const string http_response(GetNoUpdateResponse(UpdateCheckParams::kAppId));
211
212 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
213
214 UpdateCheckAction action(params,
215 new MockHttpFetcher(http_response.data(),
216 http_response.size()));
217 UpdateCheckActionTestProcessorDelegate delegate;
218 delegate.loop_ = loop;
219 ActionProcessor processor;
220 processor.set_delegate(&delegate);
221 processor.EnqueueAction(&action);
222
223 g_timeout_add(0, &StartProcessorInRunLoop, &processor);
224 g_main_loop_run(loop);
225 g_main_loop_unref(loop);
226 EXPECT_FALSE(processor.IsRunning());
227}
228
229TEST(UpdateCheckActionTest, InvalidXmlTest) {
230 UpdateCheckParams params("machine_id",
231 "user_id",
232 UpdateCheckParams::kOsPlatform,
233 UpdateCheckParams::kOsVersion,
234 "service_pack",
235 UpdateCheckParams::kAppId,
236 "0.1.0.0",
237 "en-US",
238 "unittest_track");
239 UpdateCheckResponse response;
240 ASSERT_TRUE(
241 TestUpdateCheckAction(params,
242 "invalid xml>",
243 false,
244 &response,
245 NULL));
246 EXPECT_FALSE(response.update_exists);
247}
248
249TEST(UpdateCheckActionTest, MissingStatusTest) {
250 UpdateCheckParams params("machine_id",
251 "user_id",
252 UpdateCheckParams::kOsPlatform,
253 UpdateCheckParams::kOsVersion,
254 "service_pack",
255 UpdateCheckParams::kAppId,
256 "0.1.0.0",
257 "en-US",
258 "unittest_track");
259 UpdateCheckResponse response;
260 ASSERT_TRUE(TestUpdateCheckAction(
261 params,
262 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
263 "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
264 "appid=\"foo\" status=\"ok\"><ping "
265 "status=\"ok\"/><updatecheck/></app></gupdate>",
266 false,
267 &response,
268 NULL));
269 EXPECT_FALSE(response.update_exists);
270}
271
272TEST(UpdateCheckActionTest, InvalidStatusTest) {
273 UpdateCheckParams params("machine_id",
274 "user_id",
275 UpdateCheckParams::kOsPlatform,
276 UpdateCheckParams::kOsVersion,
277 "service_pack",
278 UpdateCheckParams::kAppId,
279 "0.1.0.0",
280 "en-US",
281 "unittest_track");
282 UpdateCheckResponse response;
283 ASSERT_TRUE(TestUpdateCheckAction(
284 params,
285 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
286 "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
287 "appid=\"foo\" status=\"ok\"><ping "
288 "status=\"ok\"/><updatecheck status=\"foo\"/></app></gupdate>",
289 false,
290 &response,
291 NULL));
292 EXPECT_FALSE(response.update_exists);
293}
294
295TEST(UpdateCheckActionTest, MissingNodesetTest) {
296 UpdateCheckParams params("machine_id",
297 "user_id",
298 UpdateCheckParams::kOsPlatform,
299 UpdateCheckParams::kOsVersion,
300 "service_pack",
301 UpdateCheckParams::kAppId,
302 "0.1.0.0",
303 "en-US",
304 "unittest_track");
305 UpdateCheckResponse response;
306 ASSERT_TRUE(TestUpdateCheckAction(
307 params,
308 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
309 "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
310 "appid=\"foo\" status=\"ok\"><ping "
311 "status=\"ok\"/></app></gupdate>",
312 false,
313 &response,
314 NULL));
315 EXPECT_FALSE(response.update_exists);
316}
317
318TEST(UpdateCheckActionTest, MissingFieldTest) {
319 UpdateCheckParams params("machine_id",
320 "user_id",
321 UpdateCheckParams::kOsPlatform,
322 UpdateCheckParams::kOsVersion,
323 "service_pack",
324 UpdateCheckParams::kAppId,
325 "0.1.0.0",
326 "en-US",
327 "unittest_track");
328 UpdateCheckResponse response;
329 ASSERT_TRUE(TestUpdateCheckAction(params,
330 string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
331 "xmlns=\"http://www.google.com/update2/response\" "
332 "protocol=\"2.0\"><app appid=\"") +
333 UpdateCheckParams::kAppId + "\" status=\"ok\"><ping "
334 "status=\"ok\"/><updatecheck DisplayVersion=\"1.2.3.4\" "
335 "Prompt=\"false\" "
336 "codebase=\"http://code/base\" "
337 "hash=\"HASH1234=\" needsadmin=\"true\" "
338 "size=\"123\" status=\"ok\"/></app></gupdate>",
339 true,
340 &response,
341 NULL));
342 EXPECT_TRUE(response.update_exists);
343 EXPECT_EQ("1.2.3.4", response.display_version);
344 EXPECT_EQ("http://code/base", response.codebase);
345 EXPECT_EQ("", response.more_info_url);
346 EXPECT_EQ("HASH1234=", response.hash);
347 EXPECT_EQ(123, response.size);
348 EXPECT_TRUE(response.needs_admin);
349 EXPECT_FALSE(response.prompt);
350}
351
352namespace {
353class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
354 public:
355 void ProcessingStopped(const ActionProcessor* processor) {
356 ASSERT_TRUE(loop_);
357 g_main_loop_quit(loop_);
358 }
359 GMainLoop *loop_;
360};
361
362gboolean TerminateTransferTestStarter(gpointer data) {
363 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
364 processor->StartProcessing();
365 CHECK(processor->IsRunning());
366 processor->StopProcessing();
367 return FALSE;
368}
369} // namespace {}
370
371TEST(UpdateCheckActionTest, TerminateTransferTest) {
372 UpdateCheckParams params("", // machine_id
373 "", // usr_id
374 UpdateCheckParams::kOsPlatform,
375 UpdateCheckParams::kOsVersion,
376 "", // os_sp
377 UpdateCheckParams::kAppId,
378 "0.1.0.0",
379 "en-US",
380 "unittest");
381 string http_response("doesn't matter");
382 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
383
384 UpdateCheckAction action(params,
385 new MockHttpFetcher(http_response.data(),
386 http_response.size()));
387 TerminateEarlyTestProcessorDelegate delegate;
388 delegate.loop_ = loop;
389 ActionProcessor processor;
390 processor.set_delegate(&delegate);
391 processor.EnqueueAction(&action);
392
393 g_timeout_add(0, &TerminateTransferTestStarter, &processor);
394 g_main_loop_run(loop);
395 g_main_loop_unref(loop);
396}
397
398TEST(UpdateCheckActionTest, XmlEncodeTest) {
399 EXPECT_EQ("ab", XmlEncode("ab"));
400 EXPECT_EQ("a&lt;b", XmlEncode("a<b"));
401 EXPECT_EQ("foo-&#x3A9;", XmlEncode("foo-\xce\xa9"));
402 EXPECT_EQ("&lt;&amp;&gt;", XmlEncode("<&>"));
403 EXPECT_EQ("&amp;lt;&amp;amp;&amp;gt;", XmlEncode("&lt;&amp;&gt;"));
404
405 vector<char> post_data;
406
407 // Make sure XML Encode is being called on the params
408 UpdateCheckParams params("testthemachine<id",
409 "testtheuser_id&lt;",
410 UpdateCheckParams::kOsPlatform,
411 UpdateCheckParams::kOsVersion,
412 "testtheservice_pack>",
413 UpdateCheckParams::kAppId,
414 "0.1.0.0",
415 "en-US",
416 "unittest_track");
417 UpdateCheckResponse response;
418 ASSERT_TRUE(
419 TestUpdateCheckAction(params,
420 "invalid xml>",
421 false,
422 &response,
423 &post_data));
424 // convert post_data to string
425 string post_str(&post_data[0], post_data.size());
426 EXPECT_NE(post_str.find("testthemachine&lt;id"), string::npos);
427 EXPECT_EQ(post_str.find("testthemachine<id"), string::npos);
428 EXPECT_NE(post_str.find("testtheuser_id&amp;lt;"), string::npos);
429 EXPECT_EQ(post_str.find("testtheuser_id&lt;"), string::npos);
430 EXPECT_NE(post_str.find("testtheservice_pack&gt;"), string::npos);
431 EXPECT_EQ(post_str.find("testtheservice_pack>"), string::npos);
432}
433
434TEST(UpdateCheckActionTest, XmlDecodeTest) {
435 UpdateCheckParams params("machine_id",
436 "user_id",
437 UpdateCheckParams::kOsPlatform,
438 UpdateCheckParams::kOsVersion,
439 "service_pack",
440 UpdateCheckParams::kAppId,
441 "0.1.0.0",
442 "en-US",
443 "unittest_track");
444 UpdateCheckResponse response;
445 ASSERT_TRUE(
446 TestUpdateCheckAction(params,
447 GetUpdateResponse(UpdateCheckParams::kAppId,
448 "1.2.3.4", // version
449 "testthe&lt;url", // more info
450 "true", // prompt
451 "testthe&amp;codebase", // dl url
452 "HASH1234=", // checksum
453 "false", // needs admin
454 "123"), // size
455 true,
456 &response,
457 NULL));
458
459 EXPECT_EQ(response.more_info_url, "testthe<url");
460 EXPECT_EQ(response.codebase, "testthe&codebase");
461}
462
463TEST(UpdateCheckActionTest, ParseIntTest) {
464 UpdateCheckParams params("machine_id",
465 "user_id",
466 UpdateCheckParams::kOsPlatform,
467 UpdateCheckParams::kOsVersion,
468 "service_pack",
469 UpdateCheckParams::kAppId,
470 "0.1.0.0",
471 "en-US",
472 "unittest_track");
473 UpdateCheckResponse response;
474 ASSERT_TRUE(
475 TestUpdateCheckAction(params,
476 GetUpdateResponse(UpdateCheckParams::kAppId,
477 "1.2.3.4", // version
478 "theurl", // more info
479 "true", // prompt
480 "thecodebase", // dl url
481 "HASH1234=", // checksum
482 "false", // needs admin
483 // overflows int32:
484 "123123123123123"), // size
485 true,
486 &response,
487 NULL));
488
489 EXPECT_EQ(response.size, 123123123123123ll);
490}
491
492} // namespace chromeos_update_engine