blob: eecd456ccfe72116829b3996eaaf536eb1479386 [file] [log] [blame]
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001#!/usr/bin/python2
Gilad Arnold5502b562013-03-08 13:22:31 -08002#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit testing checker.py."""
8
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08009from __future__ import print_function
10
Gilad Arnold5502b562013-03-08 13:22:31 -080011import array
12import collections
13import cStringIO
14import hashlib
15import itertools
16import os
17import unittest
18
Gilad Arnoldcb638912013-06-24 04:57:11 -070019# pylint cannot find mox.
Gilad Arnold5502b562013-03-08 13:22:31 -080020# pylint: disable=F0401
21import mox
22
23import checker
24import common
Gilad Arnoldcb638912013-06-24 04:57:11 -070025import payload as update_payload # Avoid name conflicts later.
Gilad Arnold5502b562013-03-08 13:22:31 -080026import test_utils
27import update_metadata_pb2
28
29
Gilad Arnold5502b562013-03-08 13:22:31 -080030def _OpTypeByName(op_name):
31 op_name_to_type = {
32 'REPLACE': common.OpType.REPLACE,
33 'REPLACE_BZ': common.OpType.REPLACE_BZ,
34 'MOVE': common.OpType.MOVE,
35 'BSDIFF': common.OpType.BSDIFF,
Allie Woodf5c4f3e2015-02-20 16:57:46 -080036 'SOURCE_COPY': common.OpType.SOURCE_COPY,
37 'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
Alex Deymo28466772015-09-11 17:16:44 -070038 'ZERO': common.OpType.ZERO,
39 'DISCARD': common.OpType.DISCARD,
40 'REPLACE_XZ': common.OpType.REPLACE_XZ,
Amin Hassani5ef5d452017-08-04 13:10:59 -070041 'PUFFDIFF': common.OpType.PUFFDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080042 }
43 return op_name_to_type[op_name]
44
45
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070046def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
47 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080048 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070049 if payload_gen_dargs is None:
50 payload_gen_dargs = {}
51 if checker_init_dargs is None:
52 checker_init_dargs = {}
53
Gilad Arnold5502b562013-03-08 13:22:31 -080054 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070055 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080056 payload_file.seek(0)
57 payload = update_payload.Payload(payload_file)
58 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070059 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080060
61
62def _GetPayloadCheckerWithData(payload_gen):
63 """Returns a payload checker from a given payload generator."""
64 payload_file = cStringIO.StringIO()
65 payload_gen.WriteToFile(payload_file)
66 payload_file.seek(0)
67 payload = update_payload.Payload(payload_file)
68 payload.Init()
69 return checker.PayloadChecker(payload)
70
71
Gilad Arnoldcb638912013-06-24 04:57:11 -070072# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080073# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070074# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080075# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070076# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080077# pylint: disable=E1101
78class PayloadCheckerTest(mox.MoxTestBase):
79 """Tests the PayloadChecker class.
80
81 In addition to ordinary testFoo() methods, which are automatically invoked by
82 the unittest framework, in this class we make use of DoBarTest() calls that
83 implement parametric tests of certain features. In order to invoke each test,
84 which embodies a unique combination of parameter values, as a complete unit
85 test, we perform explicit enumeration of the parameter space and create
86 individual invocation contexts for each, which are then bound as
87 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
88 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -080089 """
90
91 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -080092 """Create a mock payload object, complete with a mock manifest."""
Gilad Arnold5502b562013-03-08 13:22:31 -080093 payload = self.mox.CreateMock(update_payload.Payload)
94 payload.is_init = True
95 payload.manifest = self.mox.CreateMock(
96 update_metadata_pb2.DeltaArchiveManifest)
97 return payload
98
99 @staticmethod
100 def NewExtent(start_block, num_blocks):
101 """Returns an Extent message.
102
103 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
104 its default state.
105
106 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700107 start_block: The starting block of the extent.
108 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800109
Gilad Arnold5502b562013-03-08 13:22:31 -0800110 Returns:
111 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800112 """
113 ex = update_metadata_pb2.Extent()
114 if start_block >= 0:
115 ex.start_block = start_block
116 if num_blocks >= 0:
117 ex.num_blocks = num_blocks
118 return ex
119
120 @staticmethod
121 def NewExtentList(*args):
122 """Returns an list of extents.
123
124 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700125 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800126
Gilad Arnold5502b562013-03-08 13:22:31 -0800127 Returns:
128 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800129 """
130 ex_list = []
131 for start_block, num_blocks in args:
132 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
133 return ex_list
134
135 @staticmethod
136 def AddToMessage(repeated_field, field_vals):
137 for field_val in field_vals:
138 new_field = repeated_field.add()
139 new_field.CopyFrom(field_val)
140
Gilad Arnold5502b562013-03-08 13:22:31 -0800141 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
142 linebreak=False, indent=0):
143 """Setup for testing of _CheckElem() and its derivatives.
144
145 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700146 is_present: Whether or not the element is found in the message.
147 is_submsg: Whether the element is a sub-message itself.
148 convert: A representation conversion function.
149 linebreak: Whether or not a linebreak is to be used in the report.
150 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800151
Gilad Arnold5502b562013-03-08 13:22:31 -0800152 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700153 msg: A mock message object.
154 report: A mock report object.
155 subreport: A mock sub-report object.
156 name: An element name to check.
157 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800158 """
159 name = 'foo'
160 val = 'fake submsg' if is_submsg else 'fake field'
161 subreport = 'fake subreport'
162
163 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700164 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800165 msg.HasField(name).AndReturn(is_present)
166 setattr(msg, name, val)
167
168 # Create a mock report.
169 report = self.mox.CreateMock(checker._PayloadReport)
170 if is_present:
171 if is_submsg:
172 report.AddSubReport(name).AndReturn(subreport)
173 else:
174 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
175
176 self.mox.ReplayAll()
177 return (msg, report, subreport, name, val)
178
179 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
180 linebreak, indent):
181 """Parametric testing of _CheckElem().
182
183 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700184 is_present: Whether or not the element is found in the message.
185 is_mandatory: Whether or not it's a mandatory element.
186 is_submsg: Whether the element is a sub-message itself.
187 convert: A representation conversion function.
188 linebreak: Whether or not a linebreak is to be used in the report.
189 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800190 """
191 msg, report, subreport, name, val = self.SetupAddElemTest(
192 is_present, is_submsg, convert, linebreak, indent)
193
Gilad Arnoldcb638912013-06-24 04:57:11 -0700194 args = (msg, name, report, is_mandatory, is_submsg)
195 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800196 if is_mandatory and not is_present:
197 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700198 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800199 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700200 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
201 **kwargs)
202 self.assertEquals(val if is_present else None, ret_val)
203 self.assertEquals(subreport if is_present and is_submsg else None,
204 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800205
206 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
207 indent):
208 """Parametric testing of _Check{Mandatory,Optional}Field().
209
210 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700211 is_mandatory: Whether we're testing a mandatory call.
212 is_present: Whether or not the element is found in the message.
213 convert: A representation conversion function.
214 linebreak: Whether or not a linebreak is to be used in the report.
215 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800216 """
217 msg, report, _, name, val = self.SetupAddElemTest(
218 is_present, False, convert, linebreak, indent)
219
220 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700221 args = [msg, name, report]
222 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800223 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700224 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800225 tested_func = checker.PayloadChecker._CheckMandatoryField
226 else:
227 tested_func = checker.PayloadChecker._CheckOptionalField
228
229 # Test the method call.
230 if is_mandatory and not is_present:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700231 self.assertRaises(update_payload.PayloadError, tested_func, *args,
232 **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800233 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700234 ret_val = tested_func(*args, **kwargs)
235 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800236
237 def DoAddSubMsgTest(self, is_mandatory, is_present):
238 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
239
240 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700241 is_mandatory: Whether we're testing a mandatory call.
242 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800243 """
244 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
245
246 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700247 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800248 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700249 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800250 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
251 else:
252 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
253
254 # Test the method call.
255 if is_mandatory and not is_present:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700256 self.assertRaises(update_payload.PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800257 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700258 ret_val, ret_subreport = tested_func(*args)
259 self.assertEquals(val if is_present else None, ret_val)
260 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800261
262 def testCheckPresentIff(self):
263 """Tests _CheckPresentIff()."""
264 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
265 None, None, 'foo', 'bar', 'baz'))
266 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
267 'a', 'b', 'foo', 'bar', 'baz'))
268 self.assertRaises(update_payload.PayloadError,
269 checker.PayloadChecker._CheckPresentIff,
270 'a', None, 'foo', 'bar', 'baz')
271 self.assertRaises(update_payload.PayloadError,
272 checker.PayloadChecker._CheckPresentIff,
273 None, 'b', 'foo', 'bar', 'baz')
274
275 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
276 sig_data, sig_asn1_header,
277 returned_signed_hash, expected_signed_hash):
278 """Parametric testing of _CheckSha256SignatureTest().
279
280 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700281 expect_pass: Whether or not it should pass.
282 expect_subprocess_call: Whether to expect the openssl call to happen.
283 sig_data: The signature raw data.
284 sig_asn1_header: The ASN1 header.
285 returned_signed_hash: The signed hash data retuned by openssl.
286 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800287 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700288 try:
289 # Stub out the subprocess invocation.
290 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
291 if expect_subprocess_call:
292 checker.PayloadChecker._Run(
293 mox.IsA(list), send_data=sig_data).AndReturn(
294 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800295
Gilad Arnoldcb638912013-06-24 04:57:11 -0700296 self.mox.ReplayAll()
297 if expect_pass:
298 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
299 sig_data, 'foo', expected_signed_hash, 'bar'))
300 else:
301 self.assertRaises(update_payload.PayloadError,
302 checker.PayloadChecker._CheckSha256Signature,
303 sig_data, 'foo', expected_signed_hash, 'bar')
304 finally:
305 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800306
307 def testCheckSha256Signature_Pass(self):
308 """Tests _CheckSha256Signature(); pass case."""
309 sig_data = 'fake-signature'.ljust(256)
310 signed_hash = hashlib.sha256('fake-data').digest()
311 self.DoCheckSha256SignatureTest(True, True, sig_data,
312 common.SIG_ASN1_HEADER, signed_hash,
313 signed_hash)
314
315 def testCheckSha256Signature_FailBadSignature(self):
316 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700317 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800318 signed_hash = hashlib.sha256('fake-data').digest()
319 self.DoCheckSha256SignatureTest(False, False, sig_data,
320 common.SIG_ASN1_HEADER, signed_hash,
321 signed_hash)
322
323 def testCheckSha256Signature_FailBadOutputLength(self):
324 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
325 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700326 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800327 self.DoCheckSha256SignatureTest(False, True, sig_data,
328 common.SIG_ASN1_HEADER, signed_hash,
329 signed_hash)
330
331 def testCheckSha256Signature_FailBadAsnHeader(self):
332 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
333 sig_data = 'fake-signature'.ljust(256)
334 signed_hash = hashlib.sha256('fake-data').digest()
335 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
336 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
337 signed_hash, signed_hash)
338
339 def testCheckSha256Signature_FailBadHash(self):
340 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
341 sig_data = 'fake-signature'.ljust(256)
342 expected_signed_hash = hashlib.sha256('fake-data').digest()
343 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
344 self.DoCheckSha256SignatureTest(False, True, sig_data,
345 common.SIG_ASN1_HEADER,
346 expected_signed_hash, returned_signed_hash)
347
348 def testCheckBlocksFitLength_Pass(self):
349 """Tests _CheckBlocksFitLength(); pass case."""
350 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
351 64, 4, 16, 'foo'))
352 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
353 60, 4, 16, 'foo'))
354 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
355 49, 4, 16, 'foo'))
356 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
357 48, 3, 16, 'foo'))
358
359 def testCheckBlocksFitLength_TooManyBlocks(self):
360 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
361 self.assertRaises(update_payload.PayloadError,
362 checker.PayloadChecker._CheckBlocksFitLength,
363 64, 5, 16, 'foo')
364 self.assertRaises(update_payload.PayloadError,
365 checker.PayloadChecker._CheckBlocksFitLength,
366 60, 5, 16, 'foo')
367 self.assertRaises(update_payload.PayloadError,
368 checker.PayloadChecker._CheckBlocksFitLength,
369 49, 5, 16, 'foo')
370 self.assertRaises(update_payload.PayloadError,
371 checker.PayloadChecker._CheckBlocksFitLength,
372 48, 4, 16, 'foo')
373
374 def testCheckBlocksFitLength_TooFewBlocks(self):
375 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
376 self.assertRaises(update_payload.PayloadError,
377 checker.PayloadChecker._CheckBlocksFitLength,
378 64, 3, 16, 'foo')
379 self.assertRaises(update_payload.PayloadError,
380 checker.PayloadChecker._CheckBlocksFitLength,
381 60, 3, 16, 'foo')
382 self.assertRaises(update_payload.PayloadError,
383 checker.PayloadChecker._CheckBlocksFitLength,
384 49, 3, 16, 'foo')
385 self.assertRaises(update_payload.PayloadError,
386 checker.PayloadChecker._CheckBlocksFitLength,
387 48, 2, 16, 'foo')
388
389 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
390 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800391 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
392 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
393 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800394 """Parametric testing of _CheckManifest().
395
396 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700397 fail_mismatched_block_size: Simulate a missing block_size field.
398 fail_bad_sigs: Make signatures descriptor inconsistent.
399 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
400 fail_bad_oki: Tamper with old kernel info.
401 fail_bad_ori: Tamper with old rootfs info.
402 fail_bad_nki: Tamper with new kernel info.
403 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700404 fail_old_kernel_fs_size: Make old kernel fs size too big.
405 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
406 fail_new_kernel_fs_size: Make new kernel fs size too big.
407 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800408 """
409 # Generate a test payload. For this test, we only care about the manifest
410 # and don't need any data blobs, hence we can use a plain paylaod generator
411 # (which also gives us more control on things that can be screwed up).
412 payload_gen = test_utils.PayloadGenerator()
413
414 # Tamper with block size, if required.
415 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700416 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800417 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700418 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800419
420 # Add some operations.
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800421 payload_gen.AddOperation(False, common.OpType.MOVE,
422 src_extents=[(0, 16), (16, 497)],
423 dst_extents=[(16, 496), (0, 16)])
424 payload_gen.AddOperation(True, common.OpType.MOVE,
425 src_extents=[(0, 8), (8, 8)],
426 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800427
428 # Set an invalid signatures block (offset but no size), if required.
429 if fail_bad_sigs:
430 payload_gen.SetSignatures(32, None)
431
Gilad Arnold382df5c2013-05-03 12:49:28 -0700432 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700433 rootfs_part_size = test_utils.MiB(8)
434 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700435 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
436 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
437 if fail_old_kernel_fs_size:
438 old_kernel_fs_size += 100
439 if fail_old_rootfs_fs_size:
440 old_rootfs_fs_size += 100
441 if fail_new_kernel_fs_size:
442 new_kernel_fs_size += 100
443 if fail_new_rootfs_fs_size:
444 new_rootfs_fs_size += 100
445
Gilad Arnold5502b562013-03-08 13:22:31 -0800446 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700447 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800448 oki_hash = (None if fail_bad_oki
449 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700450 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
451 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
452 fail_bad_ori):
453 ori_hash = (None if fail_bad_ori
454 else hashlib.sha256('fake-ori-content').digest())
455 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800456
457 # Add new kernel/rootfs partition info.
458 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700459 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800460 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
461 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700462 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800463 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
464
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700465 # Set the minor version.
466 payload_gen.SetMinorVersion(0)
467
Gilad Arnold5502b562013-03-08 13:22:31 -0800468 # Create the test object.
469 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
470 report = checker._PayloadReport()
471
472 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
473 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800474 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
475 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
476 fail_new_rootfs_fs_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800477 if should_fail:
478 self.assertRaises(update_payload.PayloadError,
Gilad Arnold382df5c2013-05-03 12:49:28 -0700479 payload_checker._CheckManifest, report,
480 rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800481 else:
Gilad Arnold382df5c2013-05-03 12:49:28 -0700482 self.assertIsNone(payload_checker._CheckManifest(report,
483 rootfs_part_size,
484 kernel_part_size))
Gilad Arnold5502b562013-03-08 13:22:31 -0800485
486 def testCheckLength(self):
487 """Tests _CheckLength()."""
488 payload_checker = checker.PayloadChecker(self.MockPayload())
489 block_size = payload_checker.block_size
490
491 # Passes.
492 self.assertIsNone(payload_checker._CheckLength(
493 int(3.5 * block_size), 4, 'foo', 'bar'))
494 # Fails, too few blocks.
495 self.assertRaises(update_payload.PayloadError,
496 payload_checker._CheckLength,
497 int(3.5 * block_size), 3, 'foo', 'bar')
498 # Fails, too many blocks.
499 self.assertRaises(update_payload.PayloadError,
500 payload_checker._CheckLength,
501 int(3.5 * block_size), 5, 'foo', 'bar')
502
503 def testCheckExtents(self):
504 """Tests _CheckExtents()."""
505 payload_checker = checker.PayloadChecker(self.MockPayload())
506 block_size = payload_checker.block_size
507
508 # Passes w/ all real extents.
509 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
510 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700511 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800512 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700513 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800514
515 # Passes w/ pseudo-extents (aka sparse holes).
516 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
517 (8, 3))
518 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700519 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800520 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
521 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700522 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800523
524 # Passes w/ pseudo-extent due to a signature.
525 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
526 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700527 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800528 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
529 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700530 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800531
532 # Fails, extent missing a start block.
533 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
534 self.assertRaises(
535 update_payload.PayloadError, payload_checker._CheckExtents,
536 extents, (1024 + 16) * block_size, collections.defaultdict(int),
537 'foo')
538
539 # Fails, extent missing block count.
540 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
541 self.assertRaises(
542 update_payload.PayloadError, payload_checker._CheckExtents,
543 extents, (1024 + 16) * block_size, collections.defaultdict(int),
544 'foo')
545
546 # Fails, extent has zero blocks.
547 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
548 self.assertRaises(
549 update_payload.PayloadError, payload_checker._CheckExtents,
550 extents, (1024 + 16) * block_size, collections.defaultdict(int),
551 'foo')
552
553 # Fails, extent exceeds partition boundaries.
554 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
555 self.assertRaises(
556 update_payload.PayloadError, payload_checker._CheckExtents,
557 extents, (1024 + 15) * block_size, collections.defaultdict(int),
558 'foo')
559
560 def testCheckReplaceOperation(self):
561 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
562 payload_checker = checker.PayloadChecker(self.MockPayload())
563 block_size = payload_checker.block_size
564 data_length = 10000
565
566 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700567 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800568 op.type = common.OpType.REPLACE
569
570 # Pass.
571 op.src_extents = []
572 self.assertIsNone(
573 payload_checker._CheckReplaceOperation(
574 op, data_length, (data_length + block_size - 1) / block_size,
575 'foo'))
576
577 # Fail, src extents founds.
578 op.src_extents = ['bar']
579 self.assertRaises(
580 update_payload.PayloadError,
581 payload_checker._CheckReplaceOperation,
582 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
583
584 # Fail, missing data.
585 op.src_extents = []
586 self.assertRaises(
587 update_payload.PayloadError,
588 payload_checker._CheckReplaceOperation,
589 op, None, (data_length + block_size - 1) / block_size, 'foo')
590
591 # Fail, length / block number mismatch.
592 op.src_extents = ['bar']
593 self.assertRaises(
594 update_payload.PayloadError,
595 payload_checker._CheckReplaceOperation,
596 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
597
598 def testCheckReplaceBzOperation(self):
599 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
600 payload_checker = checker.PayloadChecker(self.MockPayload())
601 block_size = payload_checker.block_size
602 data_length = block_size * 3
603
604 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700605 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800606 op.type = common.OpType.REPLACE_BZ
607
608 # Pass.
609 op.src_extents = []
610 self.assertIsNone(
611 payload_checker._CheckReplaceOperation(
612 op, data_length, (data_length + block_size - 1) / block_size + 5,
613 'foo'))
614
615 # Fail, src extents founds.
616 op.src_extents = ['bar']
617 self.assertRaises(
618 update_payload.PayloadError,
619 payload_checker._CheckReplaceOperation,
620 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
621
622 # Fail, missing data.
623 op.src_extents = []
624 self.assertRaises(
625 update_payload.PayloadError,
626 payload_checker._CheckReplaceOperation,
627 op, None, (data_length + block_size - 1) / block_size, 'foo')
628
629 # Fail, too few blocks to justify BZ.
630 op.src_extents = []
631 self.assertRaises(
632 update_payload.PayloadError,
633 payload_checker._CheckReplaceOperation,
634 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
635
636 def testCheckMoveOperation_Pass(self):
637 """Tests _CheckMoveOperation(); pass case."""
638 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700639 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800640 op.type = common.OpType.MOVE
641
642 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700643 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800644 self.AddToMessage(op.dst_extents,
645 self.NewExtentList((16, 128), (512, 6)))
646 self.assertIsNone(
647 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
648
649 def testCheckMoveOperation_FailContainsData(self):
650 """Tests _CheckMoveOperation(); fails, message contains data."""
651 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700652 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800653 op.type = common.OpType.MOVE
654
655 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700656 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800657 self.AddToMessage(op.dst_extents,
658 self.NewExtentList((16, 128), (512, 6)))
659 self.assertRaises(
660 update_payload.PayloadError,
661 payload_checker._CheckMoveOperation,
662 op, 1024, 134, 134, 'foo')
663
664 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
665 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
666 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700667 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800668 op.type = common.OpType.MOVE
669
670 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700671 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800672 self.AddToMessage(op.dst_extents,
673 self.NewExtentList((16, 128), (512, 6)))
674 self.assertRaises(
675 update_payload.PayloadError,
676 payload_checker._CheckMoveOperation,
677 op, None, 134, 134, 'foo')
678
679 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
680 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
681 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700682 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800683 op.type = common.OpType.MOVE
684
685 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700686 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800687 self.AddToMessage(op.dst_extents,
688 self.NewExtentList((16, 128), (512, 5)))
689 self.assertRaises(
690 update_payload.PayloadError,
691 payload_checker._CheckMoveOperation,
692 op, None, 134, 134, 'foo')
693
694 def testCheckMoveOperation_FailExcessSrcBlocks(self):
695 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
696 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700697 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800698 op.type = common.OpType.MOVE
699
700 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700701 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800702 self.AddToMessage(op.dst_extents,
703 self.NewExtentList((16, 128), (512, 5)))
704 self.assertRaises(
705 update_payload.PayloadError,
706 payload_checker._CheckMoveOperation,
707 op, None, 134, 134, 'foo')
708 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700709 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800710 self.AddToMessage(op.dst_extents,
711 self.NewExtentList((16, 128), (512, 6)))
712 self.assertRaises(
713 update_payload.PayloadError,
714 payload_checker._CheckMoveOperation,
715 op, None, 134, 134, 'foo')
716
717 def testCheckMoveOperation_FailExcessDstBlocks(self):
718 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
719 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700720 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800721 op.type = common.OpType.MOVE
722
723 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700724 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800725 self.AddToMessage(op.dst_extents,
726 self.NewExtentList((16, 128), (512, 7)))
727 self.assertRaises(
728 update_payload.PayloadError,
729 payload_checker._CheckMoveOperation,
730 op, None, 134, 134, 'foo')
731
732 def testCheckMoveOperation_FailStagnantBlocks(self):
733 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
734 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700735 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800736 op.type = common.OpType.MOVE
737
738 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700739 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
740 self.AddToMessage(op.dst_extents,
741 self.NewExtentList((8, 128), (512, 6)))
742 self.assertRaises(
743 update_payload.PayloadError,
744 payload_checker._CheckMoveOperation,
745 op, None, 134, 134, 'foo')
746
747 def testCheckMoveOperation_FailZeroStartBlock(self):
748 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
749 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700750 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700751 op.type = common.OpType.MOVE
752
753 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800754 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
755 self.AddToMessage(op.dst_extents,
756 self.NewExtentList((8, 128), (512, 6)))
757 self.assertRaises(
758 update_payload.PayloadError,
759 payload_checker._CheckMoveOperation,
760 op, None, 134, 134, 'foo')
761
Allie Woodb065e132015-04-24 10:20:27 -0700762 self.AddToMessage(op.src_extents,
763 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
764 self.AddToMessage(op.dst_extents,
765 self.NewExtentList((0, 128), (512, 6)))
766 self.assertRaises(
767 update_payload.PayloadError,
768 payload_checker._CheckMoveOperation,
769 op, None, 134, 134, 'foo')
770
Sen Jiang92161a72016-06-28 16:09:38 -0700771 def testCheckAnyDiff(self):
772 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800773 payload_checker = checker.PayloadChecker(self.MockPayload())
774
775 # Pass.
776 self.assertIsNone(
Sen Jiang92161a72016-06-28 16:09:38 -0700777 payload_checker._CheckAnyDiffOperation(10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800778
779 # Fail, missing data blob.
780 self.assertRaises(
781 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700782 payload_checker._CheckAnyDiffOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800783 None, 3, 'foo')
784
785 # Fail, too big of a diff blob (unjustified).
786 self.assertRaises(
787 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700788 payload_checker._CheckAnyDiffOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800789 10000, 2, 'foo')
790
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800791 def testCheckSourceCopyOperation_Pass(self):
792 """Tests _CheckSourceCopyOperation(); pass case."""
793 payload_checker = checker.PayloadChecker(self.MockPayload())
794 self.assertIsNone(
795 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
796
797 def testCheckSourceCopyOperation_FailContainsData(self):
798 """Tests _CheckSourceCopyOperation(); message contains data."""
799 payload_checker = checker.PayloadChecker(self.MockPayload())
800 self.assertRaises(update_payload.PayloadError,
801 payload_checker._CheckSourceCopyOperation,
802 134, 0, 0, 'foo')
803
804 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
805 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
806 payload_checker = checker.PayloadChecker(self.MockPayload())
807 self.assertRaises(update_payload.PayloadError,
808 payload_checker._CheckSourceCopyOperation,
809 None, 0, 1, 'foo')
810
Gilad Arnold5502b562013-03-08 13:22:31 -0800811 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
812 allow_unhashed, fail_src_extents, fail_dst_extents,
813 fail_mismatched_data_offset_length,
814 fail_missing_dst_extents, fail_src_length,
815 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800816 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800817 """Parametric testing of _CheckOperation().
818
819 Args:
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800820 op_type_name: 'REPLACE', 'REPLACE_BZ', 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700821 'SOURCE_BSDIFF' or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700822 is_last: Whether we're testing the last operation in a sequence.
823 allow_signature: Whether we're testing a signature-capable operation.
824 allow_unhashed: Whether we're allowing to not hash the data.
825 fail_src_extents: Tamper with src extents.
826 fail_dst_extents: Tamper with dst extents.
827 fail_mismatched_data_offset_length: Make data_{offset,length}
828 inconsistent.
829 fail_missing_dst_extents: Do not include dst extents.
830 fail_src_length: Make src length inconsistent.
831 fail_dst_length: Make dst length inconsistent.
832 fail_data_hash: Tamper with the data blob hash.
833 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800834 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800835 """
836 op_type = _OpTypeByName(op_type_name)
837
838 # Create the test object.
839 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700840 payload_checker = checker.PayloadChecker(payload,
841 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800842 block_size = payload_checker.block_size
843
844 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700845 old_part_size = test_utils.MiB(4)
846 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800847 old_block_counters = array.array(
848 'B', [0] * ((old_part_size + block_size - 1) / block_size))
849 new_block_counters = array.array(
850 'B', [0] * ((new_part_size + block_size - 1) / block_size))
851 prev_data_offset = 1876
852 blob_hash_counts = collections.defaultdict(int)
853
854 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700855 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800856 op.type = op_type
857
858 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800859 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700860 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
861 common.OpType.PUFFDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800862 if fail_src_extents:
863 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700864 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800865 else:
866 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700867 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800868 total_src_blocks = 16
869
Allie Wood7cf9f132015-02-26 14:28:19 -0800870 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700871 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800872 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700873 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800874 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700875 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700876 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
877 common.OpType.PUFFDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700878 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800879
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800880 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800881 if not fail_mismatched_data_offset_length:
882 op.data_length = 16 * block_size - 8
883 if fail_prev_data_offset:
884 op.data_offset = prev_data_offset + 16
885 else:
886 op.data_offset = prev_data_offset
887
888 fake_data = 'fake-data'.ljust(op.data_length)
889 if not (allow_unhashed or (is_last and allow_signature and
890 op_type == common.OpType.REPLACE)):
891 if not fail_data_hash:
892 # Create a valid data blob hash.
893 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
894 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
895 fake_data)
896 elif fail_data_hash:
897 # Create an invalid data blob hash.
898 op.data_sha256_hash = hashlib.sha256(
899 fake_data.replace(' ', '-')).digest()
900 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
901 fake_data)
902
903 total_dst_blocks = 0
904 if not fail_missing_dst_extents:
905 total_dst_blocks = 16
906 if fail_dst_extents:
907 self.AddToMessage(op.dst_extents,
908 self.NewExtentList((4, 16), (32, 0)))
909 else:
910 self.AddToMessage(op.dst_extents,
911 self.NewExtentList((4, 8), (64, 8)))
912
913 if total_src_blocks:
914 if fail_src_length:
915 op.src_length = total_src_blocks * block_size + 8
916 else:
917 op.src_length = total_src_blocks * block_size
918 elif fail_src_length:
919 # Add an orphaned src_length.
920 op.src_length = 16
921
922 if total_dst_blocks:
923 if fail_dst_length:
924 op.dst_length = total_dst_blocks * block_size + 8
925 else:
926 op.dst_length = total_dst_blocks * block_size
927
928 self.mox.ReplayAll()
929 should_fail = (fail_src_extents or fail_dst_extents or
930 fail_mismatched_data_offset_length or
931 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800932 fail_dst_length or fail_data_hash or fail_prev_data_offset or
933 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700934 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
935 old_part_size, new_part_size, prev_data_offset, allow_signature,
936 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800937 if should_fail:
938 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700939 payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800940 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700941 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
942 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800943
944 def testAllocBlockCounters(self):
945 """Tests _CheckMoveOperation()."""
946 payload_checker = checker.PayloadChecker(self.MockPayload())
947 block_size = payload_checker.block_size
948
949 # Check allocation for block-aligned partition size, ensure it's integers.
950 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700951 self.assertEqual(16, len(result))
952 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800953
954 # Check allocation of unaligned partition sizes.
955 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700956 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800957 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700958 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800959
Allie Woodfb04d302015-04-03 14:25:48 -0700960 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Gilad Arnold5502b562013-03-08 13:22:31 -0800961 # Generate a test payload. For this test, we only care about one
962 # (arbitrary) set of operations, so we'll only be generating kernel and
963 # test with them.
964 payload_gen = test_utils.PayloadGenerator()
965
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700966 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800967 payload_gen.SetBlockSize(block_size)
968
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700969 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800970
971 # Fake rootfs operations in a full update, tampered with as required.
972 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -0800973 rootfs_data_length = rootfs_part_size
974 if fail_nonexhaustive_full_update:
975 rootfs_data_length -= block_size
976
977 payload_gen.AddOperation(False, rootfs_op_type,
978 dst_extents=[(0, rootfs_data_length / block_size)],
979 data_offset=0,
980 data_length=rootfs_data_length)
981
982 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700983 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
984 checker_init_dargs={
985 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -0800986 payload_checker.payload_type = checker._TYPE_FULL
987 report = checker._PayloadReport()
988
Amin Hassaniae853742017-10-11 10:27:27 -0700989 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
990 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -0700991 if fail_nonexhaustive_full_update:
Gilad Arnold5502b562013-03-08 13:22:31 -0800992 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700993 payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800994 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700995 self.assertEqual(rootfs_data_length,
996 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800997
998 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
999 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1000 fail_unknown_sig_version, fail_incorrect_sig):
1001 # Generate a test payload. For this test, we only care about the signature
1002 # block and how it relates to the payload hash. Therefore, we're generating
1003 # a random (otherwise useless) payload for this purpose.
1004 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001005 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001006 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001007 rootfs_part_size = test_utils.MiB(2)
1008 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001009 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1010 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001011 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001012 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001013 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001014 payload_gen.AddOperationWithData(
1015 False, common.OpType.REPLACE,
1016 dst_extents=[(0, rootfs_part_size / block_size)],
1017 data_blob=os.urandom(rootfs_part_size))
1018
1019 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1020 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1021 fail_sig_missing_fields or fail_unknown_sig_version
1022 or fail_incorrect_sig)
1023
1024 sigs_data = None
1025 if do_forge_sigs_data:
1026 sigs_gen = test_utils.SignaturesGenerator()
1027 if not fail_empty_sigs_blob:
1028 if fail_sig_missing_fields:
1029 sig_data = None
1030 else:
1031 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001032 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001033 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1034
1035 sigs_data = sigs_gen.ToBinary()
1036 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1037
1038 if do_forge_pseudo_op:
1039 assert sigs_data is not None, 'should have forged signatures blob by now'
1040 sigs_len = len(sigs_data)
1041 payload_gen.AddOperation(
1042 False, common.OpType.REPLACE,
1043 data_offset=payload_gen.curr_offset / 2,
1044 data_length=sigs_len / 2,
1045 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1046
1047 # Generate payload (complete w/ signature) and create the test object.
1048 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001049 payload_gen.WriteToFileWithData,
1050 payload_gen_dargs={
1051 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001052 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001053 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001054 payload_checker.payload_type = checker._TYPE_FULL
1055 report = checker._PayloadReport()
1056
1057 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001058 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001059
1060 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1061 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1062 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001063 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001064 if should_fail:
1065 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001066 payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001067 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001068 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001069
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001070 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1071 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001072
1073 Args:
1074 minor_version: The payload minor version to test with.
1075 payload_type: The type of the payload we're testing, delta or full.
1076 """
1077 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001078 payload = self.MockPayload()
1079 payload.manifest.minor_version = minor_version
1080 payload_checker = checker.PayloadChecker(payload)
1081 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001082 report = checker._PayloadReport()
1083
1084 should_succeed = (
1085 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1086 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001087 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001088 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1089 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001090 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001091
1092 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001093 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001094 else:
1095 self.assertRaises(update_payload.PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001096 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001097
Gilad Arnold06eea332015-07-13 18:06:33 -07001098 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1099 fail_wrong_payload_type, fail_invalid_block_size,
1100 fail_mismatched_block_size, fail_excess_data,
1101 fail_rootfs_part_size_exceeded,
1102 fail_kernel_part_size_exceeded):
Gilad Arnold5502b562013-03-08 13:22:31 -08001103 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001104 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001105 # internal PayloadChecker methods that are tested elsewhere, here we only
1106 # tamper with what's actually being manipulated and/or tested in the Run()
1107 # method itself. Note that the checker doesn't verify partition hashes, so
1108 # they're safe to fake.
1109 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001110 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001111 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001112 kernel_filesystem_size = test_utils.KiB(16)
1113 rootfs_filesystem_size = test_utils.MiB(2)
1114 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001115 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001116 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001117 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001118 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001119
1120 rootfs_part_size = 0
1121 if rootfs_part_size_provided:
1122 rootfs_part_size = rootfs_filesystem_size + block_size
1123 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1124 if fail_rootfs_part_size_exceeded:
1125 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001126 payload_gen.AddOperationWithData(
1127 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001128 dst_extents=[(0, rootfs_op_size / block_size)],
1129 data_blob=os.urandom(rootfs_op_size))
1130
1131 kernel_part_size = 0
1132 if kernel_part_size_provided:
1133 kernel_part_size = kernel_filesystem_size + block_size
1134 kernel_op_size = kernel_part_size or kernel_filesystem_size
1135 if fail_kernel_part_size_exceeded:
1136 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001137 payload_gen.AddOperationWithData(
1138 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001139 dst_extents=[(0, kernel_op_size / block_size)],
1140 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001141
1142 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001143 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001144 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001145 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001146 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001147 else:
1148 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001149
Gilad Arnoldcb638912013-06-24 04:57:11 -07001150 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001151 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001152 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001153 'do_add_pseudo_operation': True,
1154 'is_pseudo_in_kernel': True,
1155 'padding': os.urandom(1024) if fail_excess_data else None},
1156 'checker_init_dargs': {
1157 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1158 'block_size': use_block_size}}
1159 if fail_invalid_block_size:
1160 self.assertRaises(update_payload.PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001161 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001162 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001163 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001164 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001165
1166 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1167 'rootfs_part_size': rootfs_part_size,
1168 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001169 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001170 fail_excess_data or
1171 fail_rootfs_part_size_exceeded or
1172 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001173 if should_fail:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001174 self.assertRaises(update_payload.PayloadError, payload_checker.Run,
1175 **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001176 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001177 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001178
Gilad Arnold5502b562013-03-08 13:22:31 -08001179# This implements a generic API, hence the occasional unused args.
1180# pylint: disable=W0613
1181def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1182 allow_unhashed, fail_src_extents,
1183 fail_dst_extents,
1184 fail_mismatched_data_offset_length,
1185 fail_missing_dst_extents, fail_src_length,
1186 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001187 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001188 """Returns True iff the combination of arguments represents a valid test."""
1189 op_type = _OpTypeByName(op_type_name)
1190
Allie Wood7cf9f132015-02-26 14:28:19 -08001191 # REPLACE/REPLACE_BZ operations don't read data from src partition. They are
1192 # compatible with all valid minor versions, so we don't need to check that.
Gilad Arnold5502b562013-03-08 13:22:31 -08001193 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ) and (
Allie Wood7cf9f132015-02-26 14:28:19 -08001194 fail_src_extents or fail_src_length or fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001195 return False
1196
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001197 # MOVE and SOURCE_COPY operations don't carry data.
1198 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001199 fail_mismatched_data_offset_length or fail_data_hash or
1200 fail_prev_data_offset)):
1201 return False
1202
1203 return True
1204
1205
1206def TestMethodBody(run_method_name, run_dargs):
1207 """Returns a function that invokes a named method with named arguments."""
1208 return lambda self: getattr(self, run_method_name)(**run_dargs)
1209
1210
1211def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1212 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1213
1214 This function enumerates a space of test parameters (defined by arg_space),
1215 then binds a new, unique method name in PayloadCheckerTest to a test function
1216 that gets handed the said parameters. This is a preferable approach to doing
1217 the enumeration and invocation during the tests because this way each test is
1218 treated as a complete run by the unittest framework, and so benefits from the
1219 usual setUp/tearDown mechanics.
1220
1221 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001222 tested_method_name: Name of the tested PayloadChecker method.
1223 arg_space: A dictionary containing variables (keys) and lists of values
1224 (values) associated with them.
1225 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001226 """
1227 for value_tuple in itertools.product(*arg_space.itervalues()):
1228 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1229 if validate_func and not validate_func(**run_dargs):
1230 continue
1231 run_method_name = 'Do%sTest' % tested_method_name
1232 test_method_name = 'test%s' % tested_method_name
1233 for arg_key, arg_val in run_dargs.iteritems():
1234 if arg_val or type(arg_val) is int:
1235 test_method_name += '__%s=%s' % (arg_key, arg_val)
1236 setattr(PayloadCheckerTest, test_method_name,
1237 TestMethodBody(run_method_name, run_dargs))
1238
1239
1240def AddAllParametricTests():
1241 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1242 # Add all _CheckElem() test cases.
1243 AddParametricTests('AddElem',
1244 {'linebreak': (True, False),
1245 'indent': (0, 1, 2),
1246 'convert': (str, lambda s: s[::-1]),
1247 'is_present': (True, False),
1248 'is_mandatory': (True, False),
1249 'is_submsg': (True, False)})
1250
1251 # Add all _Add{Mandatory,Optional}Field tests.
1252 AddParametricTests('AddField',
1253 {'is_mandatory': (True, False),
1254 'linebreak': (True, False),
1255 'indent': (0, 1, 2),
1256 'convert': (str, lambda s: s[::-1]),
1257 'is_present': (True, False)})
1258
1259 # Add all _Add{Mandatory,Optional}SubMsg tests.
1260 AddParametricTests('AddSubMsg',
1261 {'is_mandatory': (True, False),
1262 'is_present': (True, False)})
1263
1264 # Add all _CheckManifest() test cases.
1265 AddParametricTests('CheckManifest',
1266 {'fail_mismatched_block_size': (True, False),
1267 'fail_bad_sigs': (True, False),
1268 'fail_mismatched_oki_ori': (True, False),
1269 'fail_bad_oki': (True, False),
1270 'fail_bad_ori': (True, False),
1271 'fail_bad_nki': (True, False),
1272 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001273 'fail_old_kernel_fs_size': (True, False),
1274 'fail_old_rootfs_fs_size': (True, False),
1275 'fail_new_kernel_fs_size': (True, False),
1276 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001277
1278 # Add all _CheckOperation() test cases.
1279 AddParametricTests('CheckOperation',
1280 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'MOVE',
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001281 'BSDIFF', 'SOURCE_COPY',
Amin Hassanicdeb6e62017-10-11 10:15:11 -07001282 'SOURCE_BSDIFF', 'PUFFDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001283 'is_last': (True, False),
1284 'allow_signature': (True, False),
1285 'allow_unhashed': (True, False),
1286 'fail_src_extents': (True, False),
1287 'fail_dst_extents': (True, False),
1288 'fail_mismatched_data_offset_length': (True, False),
1289 'fail_missing_dst_extents': (True, False),
1290 'fail_src_length': (True, False),
1291 'fail_dst_length': (True, False),
1292 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001293 'fail_prev_data_offset': (True, False),
1294 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001295 validate_func=ValidateCheckOperationTest)
1296
1297 # Add all _CheckOperations() test cases.
1298 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001299 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001300
1301 # Add all _CheckOperations() test cases.
1302 AddParametricTests('CheckSignatures',
1303 {'fail_empty_sigs_blob': (True, False),
1304 'fail_missing_pseudo_op': (True, False),
1305 'fail_mismatched_pseudo_op': (True, False),
1306 'fail_sig_missing_fields': (True, False),
1307 'fail_unknown_sig_version': (True, False),
1308 'fail_incorrect_sig': (True, False)})
1309
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001310 # Add all _CheckManifestMinorVersion() test cases.
1311 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001312 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001313 'payload_type': (checker._TYPE_FULL,
1314 checker._TYPE_DELTA)})
1315
Gilad Arnold5502b562013-03-08 13:22:31 -08001316 # Add all Run() test cases.
1317 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001318 {'rootfs_part_size_provided': (True, False),
1319 'kernel_part_size_provided': (True, False),
1320 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001321 'fail_invalid_block_size': (True, False),
1322 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001323 'fail_excess_data': (True, False),
1324 'fail_rootfs_part_size_exceeded': (True, False),
1325 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001326
1327
1328if __name__ == '__main__':
1329 AddAllParametricTests()
1330 unittest.main()