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