blob: 245da55e1e8473797a30ef8ec1fe97ddb1693ad9 [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,
Amin Hassaniefa62d92017-11-09 13:46:56 -080042 'BROTLI_BSDIFF': common.OpType.BROTLI_BSDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080043 }
44 return op_name_to_type[op_name]
45
46
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070047def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
48 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080049 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070050 if payload_gen_dargs is None:
51 payload_gen_dargs = {}
52 if checker_init_dargs is None:
53 checker_init_dargs = {}
54
Gilad Arnold5502b562013-03-08 13:22:31 -080055 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070056 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080057 payload_file.seek(0)
58 payload = update_payload.Payload(payload_file)
59 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070060 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080061
62
63def _GetPayloadCheckerWithData(payload_gen):
64 """Returns a payload checker from a given payload generator."""
65 payload_file = cStringIO.StringIO()
66 payload_gen.WriteToFile(payload_file)
67 payload_file.seek(0)
68 payload = update_payload.Payload(payload_file)
69 payload.Init()
70 return checker.PayloadChecker(payload)
71
72
Gilad Arnoldcb638912013-06-24 04:57:11 -070073# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080074# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070075# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080076# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070077# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080078# pylint: disable=E1101
79class PayloadCheckerTest(mox.MoxTestBase):
80 """Tests the PayloadChecker class.
81
82 In addition to ordinary testFoo() methods, which are automatically invoked by
83 the unittest framework, in this class we make use of DoBarTest() calls that
84 implement parametric tests of certain features. In order to invoke each test,
85 which embodies a unique combination of parameter values, as a complete unit
86 test, we perform explicit enumeration of the parameter space and create
87 individual invocation contexts for each, which are then bound as
88 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
89 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -080090 """
91
92 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -080093 """Create a mock payload object, complete with a mock manifest."""
Gilad Arnold5502b562013-03-08 13:22:31 -080094 payload = self.mox.CreateMock(update_payload.Payload)
95 payload.is_init = True
96 payload.manifest = self.mox.CreateMock(
97 update_metadata_pb2.DeltaArchiveManifest)
98 return payload
99
100 @staticmethod
101 def NewExtent(start_block, num_blocks):
102 """Returns an Extent message.
103
104 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
105 its default state.
106
107 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700108 start_block: The starting block of the extent.
109 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800110
Gilad Arnold5502b562013-03-08 13:22:31 -0800111 Returns:
112 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800113 """
114 ex = update_metadata_pb2.Extent()
115 if start_block >= 0:
116 ex.start_block = start_block
117 if num_blocks >= 0:
118 ex.num_blocks = num_blocks
119 return ex
120
121 @staticmethod
122 def NewExtentList(*args):
123 """Returns an list of extents.
124
125 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700126 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800127
Gilad Arnold5502b562013-03-08 13:22:31 -0800128 Returns:
129 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800130 """
131 ex_list = []
132 for start_block, num_blocks in args:
133 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
134 return ex_list
135
136 @staticmethod
137 def AddToMessage(repeated_field, field_vals):
138 for field_val in field_vals:
139 new_field = repeated_field.add()
140 new_field.CopyFrom(field_val)
141
Gilad Arnold5502b562013-03-08 13:22:31 -0800142 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
143 linebreak=False, indent=0):
144 """Setup for testing of _CheckElem() and its derivatives.
145
146 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700147 is_present: Whether or not the element is found in the message.
148 is_submsg: Whether the element is a sub-message itself.
149 convert: A representation conversion function.
150 linebreak: Whether or not a linebreak is to be used in the report.
151 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800152
Gilad Arnold5502b562013-03-08 13:22:31 -0800153 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700154 msg: A mock message object.
155 report: A mock report object.
156 subreport: A mock sub-report object.
157 name: An element name to check.
158 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800159 """
160 name = 'foo'
161 val = 'fake submsg' if is_submsg else 'fake field'
162 subreport = 'fake subreport'
163
164 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700165 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800166 msg.HasField(name).AndReturn(is_present)
167 setattr(msg, name, val)
168
169 # Create a mock report.
170 report = self.mox.CreateMock(checker._PayloadReport)
171 if is_present:
172 if is_submsg:
173 report.AddSubReport(name).AndReturn(subreport)
174 else:
175 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
176
177 self.mox.ReplayAll()
178 return (msg, report, subreport, name, val)
179
180 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
181 linebreak, indent):
182 """Parametric testing of _CheckElem().
183
184 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700185 is_present: Whether or not the element is found in the message.
186 is_mandatory: Whether or not it's a mandatory element.
187 is_submsg: Whether the element is a sub-message itself.
188 convert: A representation conversion function.
189 linebreak: Whether or not a linebreak is to be used in the report.
190 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800191 """
192 msg, report, subreport, name, val = self.SetupAddElemTest(
193 is_present, is_submsg, convert, linebreak, indent)
194
Gilad Arnoldcb638912013-06-24 04:57:11 -0700195 args = (msg, name, report, is_mandatory, is_submsg)
196 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800197 if is_mandatory and not is_present:
198 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700199 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800200 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700201 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
202 **kwargs)
203 self.assertEquals(val if is_present else None, ret_val)
204 self.assertEquals(subreport if is_present and is_submsg else None,
205 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800206
207 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
208 indent):
209 """Parametric testing of _Check{Mandatory,Optional}Field().
210
211 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700212 is_mandatory: Whether we're testing a mandatory call.
213 is_present: Whether or not the element is found in the message.
214 convert: A representation conversion function.
215 linebreak: Whether or not a linebreak is to be used in the report.
216 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800217 """
218 msg, report, _, name, val = self.SetupAddElemTest(
219 is_present, False, convert, linebreak, indent)
220
221 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700222 args = [msg, name, report]
223 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800224 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700225 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800226 tested_func = checker.PayloadChecker._CheckMandatoryField
227 else:
228 tested_func = checker.PayloadChecker._CheckOptionalField
229
230 # Test the method call.
231 if is_mandatory and not is_present:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700232 self.assertRaises(update_payload.PayloadError, tested_func, *args,
233 **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800234 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700235 ret_val = tested_func(*args, **kwargs)
236 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800237
238 def DoAddSubMsgTest(self, is_mandatory, is_present):
239 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
240
241 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700242 is_mandatory: Whether we're testing a mandatory call.
243 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800244 """
245 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
246
247 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700248 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800249 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700250 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800251 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
252 else:
253 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
254
255 # Test the method call.
256 if is_mandatory and not is_present:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700257 self.assertRaises(update_payload.PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800258 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700259 ret_val, ret_subreport = tested_func(*args)
260 self.assertEquals(val if is_present else None, ret_val)
261 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800262
263 def testCheckPresentIff(self):
264 """Tests _CheckPresentIff()."""
265 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
266 None, None, 'foo', 'bar', 'baz'))
267 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
268 'a', 'b', 'foo', 'bar', 'baz'))
269 self.assertRaises(update_payload.PayloadError,
270 checker.PayloadChecker._CheckPresentIff,
271 'a', None, 'foo', 'bar', 'baz')
272 self.assertRaises(update_payload.PayloadError,
273 checker.PayloadChecker._CheckPresentIff,
274 None, 'b', 'foo', 'bar', 'baz')
275
276 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
277 sig_data, sig_asn1_header,
278 returned_signed_hash, expected_signed_hash):
279 """Parametric testing of _CheckSha256SignatureTest().
280
281 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700282 expect_pass: Whether or not it should pass.
283 expect_subprocess_call: Whether to expect the openssl call to happen.
284 sig_data: The signature raw data.
285 sig_asn1_header: The ASN1 header.
286 returned_signed_hash: The signed hash data retuned by openssl.
287 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800288 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700289 try:
290 # Stub out the subprocess invocation.
291 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
292 if expect_subprocess_call:
293 checker.PayloadChecker._Run(
294 mox.IsA(list), send_data=sig_data).AndReturn(
295 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800296
Gilad Arnoldcb638912013-06-24 04:57:11 -0700297 self.mox.ReplayAll()
298 if expect_pass:
299 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
300 sig_data, 'foo', expected_signed_hash, 'bar'))
301 else:
302 self.assertRaises(update_payload.PayloadError,
303 checker.PayloadChecker._CheckSha256Signature,
304 sig_data, 'foo', expected_signed_hash, 'bar')
305 finally:
306 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800307
308 def testCheckSha256Signature_Pass(self):
309 """Tests _CheckSha256Signature(); pass case."""
310 sig_data = 'fake-signature'.ljust(256)
311 signed_hash = hashlib.sha256('fake-data').digest()
312 self.DoCheckSha256SignatureTest(True, True, sig_data,
313 common.SIG_ASN1_HEADER, signed_hash,
314 signed_hash)
315
316 def testCheckSha256Signature_FailBadSignature(self):
317 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700318 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800319 signed_hash = hashlib.sha256('fake-data').digest()
320 self.DoCheckSha256SignatureTest(False, False, sig_data,
321 common.SIG_ASN1_HEADER, signed_hash,
322 signed_hash)
323
324 def testCheckSha256Signature_FailBadOutputLength(self):
325 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
326 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700327 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800328 self.DoCheckSha256SignatureTest(False, True, sig_data,
329 common.SIG_ASN1_HEADER, signed_hash,
330 signed_hash)
331
332 def testCheckSha256Signature_FailBadAsnHeader(self):
333 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
334 sig_data = 'fake-signature'.ljust(256)
335 signed_hash = hashlib.sha256('fake-data').digest()
336 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
337 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
338 signed_hash, signed_hash)
339
340 def testCheckSha256Signature_FailBadHash(self):
341 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
342 sig_data = 'fake-signature'.ljust(256)
343 expected_signed_hash = hashlib.sha256('fake-data').digest()
344 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
345 self.DoCheckSha256SignatureTest(False, True, sig_data,
346 common.SIG_ASN1_HEADER,
347 expected_signed_hash, returned_signed_hash)
348
349 def testCheckBlocksFitLength_Pass(self):
350 """Tests _CheckBlocksFitLength(); pass case."""
351 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
352 64, 4, 16, 'foo'))
353 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
354 60, 4, 16, 'foo'))
355 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
356 49, 4, 16, 'foo'))
357 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
358 48, 3, 16, 'foo'))
359
360 def testCheckBlocksFitLength_TooManyBlocks(self):
361 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
362 self.assertRaises(update_payload.PayloadError,
363 checker.PayloadChecker._CheckBlocksFitLength,
364 64, 5, 16, 'foo')
365 self.assertRaises(update_payload.PayloadError,
366 checker.PayloadChecker._CheckBlocksFitLength,
367 60, 5, 16, 'foo')
368 self.assertRaises(update_payload.PayloadError,
369 checker.PayloadChecker._CheckBlocksFitLength,
370 49, 5, 16, 'foo')
371 self.assertRaises(update_payload.PayloadError,
372 checker.PayloadChecker._CheckBlocksFitLength,
373 48, 4, 16, 'foo')
374
375 def testCheckBlocksFitLength_TooFewBlocks(self):
376 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
377 self.assertRaises(update_payload.PayloadError,
378 checker.PayloadChecker._CheckBlocksFitLength,
379 64, 3, 16, 'foo')
380 self.assertRaises(update_payload.PayloadError,
381 checker.PayloadChecker._CheckBlocksFitLength,
382 60, 3, 16, 'foo')
383 self.assertRaises(update_payload.PayloadError,
384 checker.PayloadChecker._CheckBlocksFitLength,
385 49, 3, 16, 'foo')
386 self.assertRaises(update_payload.PayloadError,
387 checker.PayloadChecker._CheckBlocksFitLength,
388 48, 2, 16, 'foo')
389
390 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
391 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800392 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
393 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
394 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800395 """Parametric testing of _CheckManifest().
396
397 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700398 fail_mismatched_block_size: Simulate a missing block_size field.
399 fail_bad_sigs: Make signatures descriptor inconsistent.
400 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
401 fail_bad_oki: Tamper with old kernel info.
402 fail_bad_ori: Tamper with old rootfs info.
403 fail_bad_nki: Tamper with new kernel info.
404 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700405 fail_old_kernel_fs_size: Make old kernel fs size too big.
406 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
407 fail_new_kernel_fs_size: Make new kernel fs size too big.
408 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800409 """
410 # Generate a test payload. For this test, we only care about the manifest
411 # and don't need any data blobs, hence we can use a plain paylaod generator
412 # (which also gives us more control on things that can be screwed up).
413 payload_gen = test_utils.PayloadGenerator()
414
415 # Tamper with block size, if required.
416 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700417 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800418 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700419 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800420
421 # Add some operations.
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800422 payload_gen.AddOperation(False, common.OpType.MOVE,
423 src_extents=[(0, 16), (16, 497)],
424 dst_extents=[(16, 496), (0, 16)])
425 payload_gen.AddOperation(True, common.OpType.MOVE,
426 src_extents=[(0, 8), (8, 8)],
427 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800428
429 # Set an invalid signatures block (offset but no size), if required.
430 if fail_bad_sigs:
431 payload_gen.SetSignatures(32, None)
432
Gilad Arnold382df5c2013-05-03 12:49:28 -0700433 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700434 rootfs_part_size = test_utils.MiB(8)
435 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700436 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
437 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
438 if fail_old_kernel_fs_size:
439 old_kernel_fs_size += 100
440 if fail_old_rootfs_fs_size:
441 old_rootfs_fs_size += 100
442 if fail_new_kernel_fs_size:
443 new_kernel_fs_size += 100
444 if fail_new_rootfs_fs_size:
445 new_rootfs_fs_size += 100
446
Gilad Arnold5502b562013-03-08 13:22:31 -0800447 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700448 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800449 oki_hash = (None if fail_bad_oki
450 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700451 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
452 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
453 fail_bad_ori):
454 ori_hash = (None if fail_bad_ori
455 else hashlib.sha256('fake-ori-content').digest())
456 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800457
458 # Add new kernel/rootfs partition info.
459 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700460 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800461 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
462 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700463 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800464 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
465
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700466 # Set the minor version.
467 payload_gen.SetMinorVersion(0)
468
Gilad Arnold5502b562013-03-08 13:22:31 -0800469 # Create the test object.
470 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
471 report = checker._PayloadReport()
472
473 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
474 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800475 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
476 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
477 fail_new_rootfs_fs_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800478 if should_fail:
479 self.assertRaises(update_payload.PayloadError,
Gilad Arnold382df5c2013-05-03 12:49:28 -0700480 payload_checker._CheckManifest, report,
481 rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800482 else:
Gilad Arnold382df5c2013-05-03 12:49:28 -0700483 self.assertIsNone(payload_checker._CheckManifest(report,
484 rootfs_part_size,
485 kernel_part_size))
Gilad Arnold5502b562013-03-08 13:22:31 -0800486
487 def testCheckLength(self):
488 """Tests _CheckLength()."""
489 payload_checker = checker.PayloadChecker(self.MockPayload())
490 block_size = payload_checker.block_size
491
492 # Passes.
493 self.assertIsNone(payload_checker._CheckLength(
494 int(3.5 * block_size), 4, 'foo', 'bar'))
495 # Fails, too few blocks.
496 self.assertRaises(update_payload.PayloadError,
497 payload_checker._CheckLength,
498 int(3.5 * block_size), 3, 'foo', 'bar')
499 # Fails, too many blocks.
500 self.assertRaises(update_payload.PayloadError,
501 payload_checker._CheckLength,
502 int(3.5 * block_size), 5, 'foo', 'bar')
503
504 def testCheckExtents(self):
505 """Tests _CheckExtents()."""
506 payload_checker = checker.PayloadChecker(self.MockPayload())
507 block_size = payload_checker.block_size
508
509 # Passes w/ all real extents.
510 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
511 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700512 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800513 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700514 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800515
516 # Passes w/ pseudo-extents (aka sparse holes).
517 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
518 (8, 3))
519 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700520 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800521 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
522 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700523 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800524
525 # Passes w/ pseudo-extent due to a signature.
526 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
527 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700528 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800529 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
530 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700531 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800532
533 # Fails, extent missing a start block.
534 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
535 self.assertRaises(
536 update_payload.PayloadError, payload_checker._CheckExtents,
537 extents, (1024 + 16) * block_size, collections.defaultdict(int),
538 'foo')
539
540 # Fails, extent missing block count.
541 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
542 self.assertRaises(
543 update_payload.PayloadError, payload_checker._CheckExtents,
544 extents, (1024 + 16) * block_size, collections.defaultdict(int),
545 'foo')
546
547 # Fails, extent has zero blocks.
548 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
549 self.assertRaises(
550 update_payload.PayloadError, payload_checker._CheckExtents,
551 extents, (1024 + 16) * block_size, collections.defaultdict(int),
552 'foo')
553
554 # Fails, extent exceeds partition boundaries.
555 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
556 self.assertRaises(
557 update_payload.PayloadError, payload_checker._CheckExtents,
558 extents, (1024 + 15) * block_size, collections.defaultdict(int),
559 'foo')
560
561 def testCheckReplaceOperation(self):
562 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
563 payload_checker = checker.PayloadChecker(self.MockPayload())
564 block_size = payload_checker.block_size
565 data_length = 10000
566
567 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700568 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800569 op.type = common.OpType.REPLACE
570
571 # Pass.
572 op.src_extents = []
573 self.assertIsNone(
574 payload_checker._CheckReplaceOperation(
575 op, data_length, (data_length + block_size - 1) / block_size,
576 'foo'))
577
578 # Fail, src extents founds.
579 op.src_extents = ['bar']
580 self.assertRaises(
581 update_payload.PayloadError,
582 payload_checker._CheckReplaceOperation,
583 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
584
585 # Fail, missing data.
586 op.src_extents = []
587 self.assertRaises(
588 update_payload.PayloadError,
589 payload_checker._CheckReplaceOperation,
590 op, None, (data_length + block_size - 1) / block_size, 'foo')
591
592 # Fail, length / block number mismatch.
593 op.src_extents = ['bar']
594 self.assertRaises(
595 update_payload.PayloadError,
596 payload_checker._CheckReplaceOperation,
597 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
598
599 def testCheckReplaceBzOperation(self):
600 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
601 payload_checker = checker.PayloadChecker(self.MockPayload())
602 block_size = payload_checker.block_size
603 data_length = block_size * 3
604
605 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700606 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800607 op.type = common.OpType.REPLACE_BZ
608
609 # Pass.
610 op.src_extents = []
611 self.assertIsNone(
612 payload_checker._CheckReplaceOperation(
613 op, data_length, (data_length + block_size - 1) / block_size + 5,
614 'foo'))
615
616 # Fail, src extents founds.
617 op.src_extents = ['bar']
618 self.assertRaises(
619 update_payload.PayloadError,
620 payload_checker._CheckReplaceOperation,
621 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
622
623 # Fail, missing data.
624 op.src_extents = []
625 self.assertRaises(
626 update_payload.PayloadError,
627 payload_checker._CheckReplaceOperation,
628 op, None, (data_length + block_size - 1) / block_size, 'foo')
629
630 # Fail, too few blocks to justify BZ.
631 op.src_extents = []
632 self.assertRaises(
633 update_payload.PayloadError,
634 payload_checker._CheckReplaceOperation,
635 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
636
637 def testCheckMoveOperation_Pass(self):
638 """Tests _CheckMoveOperation(); pass case."""
639 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700640 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800641 op.type = common.OpType.MOVE
642
643 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700644 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800645 self.AddToMessage(op.dst_extents,
646 self.NewExtentList((16, 128), (512, 6)))
647 self.assertIsNone(
648 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
649
650 def testCheckMoveOperation_FailContainsData(self):
651 """Tests _CheckMoveOperation(); fails, message contains data."""
652 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700653 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800654 op.type = common.OpType.MOVE
655
656 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700657 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800658 self.AddToMessage(op.dst_extents,
659 self.NewExtentList((16, 128), (512, 6)))
660 self.assertRaises(
661 update_payload.PayloadError,
662 payload_checker._CheckMoveOperation,
663 op, 1024, 134, 134, 'foo')
664
665 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
666 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
667 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700668 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800669 op.type = common.OpType.MOVE
670
671 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700672 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800673 self.AddToMessage(op.dst_extents,
674 self.NewExtentList((16, 128), (512, 6)))
675 self.assertRaises(
676 update_payload.PayloadError,
677 payload_checker._CheckMoveOperation,
678 op, None, 134, 134, 'foo')
679
680 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
681 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
682 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700683 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800684 op.type = common.OpType.MOVE
685
686 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700687 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800688 self.AddToMessage(op.dst_extents,
689 self.NewExtentList((16, 128), (512, 5)))
690 self.assertRaises(
691 update_payload.PayloadError,
692 payload_checker._CheckMoveOperation,
693 op, None, 134, 134, 'foo')
694
695 def testCheckMoveOperation_FailExcessSrcBlocks(self):
696 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
697 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700698 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800699 op.type = common.OpType.MOVE
700
701 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700702 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800703 self.AddToMessage(op.dst_extents,
704 self.NewExtentList((16, 128), (512, 5)))
705 self.assertRaises(
706 update_payload.PayloadError,
707 payload_checker._CheckMoveOperation,
708 op, None, 134, 134, 'foo')
709 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700710 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800711 self.AddToMessage(op.dst_extents,
712 self.NewExtentList((16, 128), (512, 6)))
713 self.assertRaises(
714 update_payload.PayloadError,
715 payload_checker._CheckMoveOperation,
716 op, None, 134, 134, 'foo')
717
718 def testCheckMoveOperation_FailExcessDstBlocks(self):
719 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
720 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700721 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800722 op.type = common.OpType.MOVE
723
724 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700725 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800726 self.AddToMessage(op.dst_extents,
727 self.NewExtentList((16, 128), (512, 7)))
728 self.assertRaises(
729 update_payload.PayloadError,
730 payload_checker._CheckMoveOperation,
731 op, None, 134, 134, 'foo')
732
733 def testCheckMoveOperation_FailStagnantBlocks(self):
734 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
735 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700736 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800737 op.type = common.OpType.MOVE
738
739 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700740 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
741 self.AddToMessage(op.dst_extents,
742 self.NewExtentList((8, 128), (512, 6)))
743 self.assertRaises(
744 update_payload.PayloadError,
745 payload_checker._CheckMoveOperation,
746 op, None, 134, 134, 'foo')
747
748 def testCheckMoveOperation_FailZeroStartBlock(self):
749 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
750 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700751 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700752 op.type = common.OpType.MOVE
753
754 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800755 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
756 self.AddToMessage(op.dst_extents,
757 self.NewExtentList((8, 128), (512, 6)))
758 self.assertRaises(
759 update_payload.PayloadError,
760 payload_checker._CheckMoveOperation,
761 op, None, 134, 134, 'foo')
762
Allie Woodb065e132015-04-24 10:20:27 -0700763 self.AddToMessage(op.src_extents,
764 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
765 self.AddToMessage(op.dst_extents,
766 self.NewExtentList((0, 128), (512, 6)))
767 self.assertRaises(
768 update_payload.PayloadError,
769 payload_checker._CheckMoveOperation,
770 op, None, 134, 134, 'foo')
771
Sen Jiang92161a72016-06-28 16:09:38 -0700772 def testCheckAnyDiff(self):
773 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800774 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800775 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800776
777 # Pass.
778 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800779 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800780
781 # Fail, missing data blob.
782 self.assertRaises(
783 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700784 payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800785 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800786
787 # Fail, too big of a diff blob (unjustified).
788 self.assertRaises(
789 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700790 payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800791 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800792
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800793 def testCheckSourceCopyOperation_Pass(self):
794 """Tests _CheckSourceCopyOperation(); pass case."""
795 payload_checker = checker.PayloadChecker(self.MockPayload())
796 self.assertIsNone(
797 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
798
799 def testCheckSourceCopyOperation_FailContainsData(self):
800 """Tests _CheckSourceCopyOperation(); message contains data."""
801 payload_checker = checker.PayloadChecker(self.MockPayload())
802 self.assertRaises(update_payload.PayloadError,
803 payload_checker._CheckSourceCopyOperation,
804 134, 0, 0, 'foo')
805
806 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
807 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
808 payload_checker = checker.PayloadChecker(self.MockPayload())
809 self.assertRaises(update_payload.PayloadError,
810 payload_checker._CheckSourceCopyOperation,
811 None, 0, 1, 'foo')
812
Gilad Arnold5502b562013-03-08 13:22:31 -0800813 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
814 allow_unhashed, fail_src_extents, fail_dst_extents,
815 fail_mismatched_data_offset_length,
816 fail_missing_dst_extents, fail_src_length,
817 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800818 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800819 """Parametric testing of _CheckOperation().
820
821 Args:
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800822 op_type_name: 'REPLACE', 'REPLACE_BZ', 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -0800823 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700824 is_last: Whether we're testing the last operation in a sequence.
825 allow_signature: Whether we're testing a signature-capable operation.
826 allow_unhashed: Whether we're allowing to not hash the data.
827 fail_src_extents: Tamper with src extents.
828 fail_dst_extents: Tamper with dst extents.
829 fail_mismatched_data_offset_length: Make data_{offset,length}
830 inconsistent.
831 fail_missing_dst_extents: Do not include dst extents.
832 fail_src_length: Make src length inconsistent.
833 fail_dst_length: Make dst length inconsistent.
834 fail_data_hash: Tamper with the data blob hash.
835 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800836 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800837 """
838 op_type = _OpTypeByName(op_type_name)
839
840 # Create the test object.
841 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700842 payload_checker = checker.PayloadChecker(payload,
843 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800844 block_size = payload_checker.block_size
845
846 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700847 old_part_size = test_utils.MiB(4)
848 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800849 old_block_counters = array.array(
850 'B', [0] * ((old_part_size + block_size - 1) / block_size))
851 new_block_counters = array.array(
852 'B', [0] * ((new_part_size + block_size - 1) / block_size))
853 prev_data_offset = 1876
854 blob_hash_counts = collections.defaultdict(int)
855
856 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700857 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800858 op.type = op_type
859
860 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800861 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700862 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800863 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800864 if fail_src_extents:
865 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700866 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800867 else:
868 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700869 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800870 total_src_blocks = 16
871
Allie Wood7cf9f132015-02-26 14:28:19 -0800872 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700873 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800874 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700875 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800876 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700877 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700878 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800879 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700880 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800881
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800882 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800883 if not fail_mismatched_data_offset_length:
884 op.data_length = 16 * block_size - 8
885 if fail_prev_data_offset:
886 op.data_offset = prev_data_offset + 16
887 else:
888 op.data_offset = prev_data_offset
889
890 fake_data = 'fake-data'.ljust(op.data_length)
891 if not (allow_unhashed or (is_last and allow_signature and
892 op_type == common.OpType.REPLACE)):
893 if not fail_data_hash:
894 # Create a valid data blob hash.
895 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
896 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
897 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800898
Gilad Arnold5502b562013-03-08 13:22:31 -0800899 elif fail_data_hash:
900 # Create an invalid data blob hash.
901 op.data_sha256_hash = hashlib.sha256(
902 fake_data.replace(' ', '-')).digest()
903 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
904 fake_data)
905
906 total_dst_blocks = 0
907 if not fail_missing_dst_extents:
908 total_dst_blocks = 16
909 if fail_dst_extents:
910 self.AddToMessage(op.dst_extents,
911 self.NewExtentList((4, 16), (32, 0)))
912 else:
913 self.AddToMessage(op.dst_extents,
914 self.NewExtentList((4, 8), (64, 8)))
915
916 if total_src_blocks:
917 if fail_src_length:
918 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800919 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
920 common.OpType.SOURCE_BSDIFF) and
921 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800922 op.src_length = total_src_blocks * block_size
923 elif fail_src_length:
924 # Add an orphaned src_length.
925 op.src_length = 16
926
927 if total_dst_blocks:
928 if fail_dst_length:
929 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800930 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
931 common.OpType.SOURCE_BSDIFF) and
932 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800933 op.dst_length = total_dst_blocks * block_size
934
935 self.mox.ReplayAll()
936 should_fail = (fail_src_extents or fail_dst_extents or
937 fail_mismatched_data_offset_length or
938 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800939 fail_dst_length or fail_data_hash or fail_prev_data_offset or
940 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700941 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
942 old_part_size, new_part_size, prev_data_offset, allow_signature,
943 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800944 if should_fail:
945 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700946 payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800947 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700948 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
949 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800950
951 def testAllocBlockCounters(self):
952 """Tests _CheckMoveOperation()."""
953 payload_checker = checker.PayloadChecker(self.MockPayload())
954 block_size = payload_checker.block_size
955
956 # Check allocation for block-aligned partition size, ensure it's integers.
957 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700958 self.assertEqual(16, len(result))
959 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800960
961 # Check allocation of unaligned partition sizes.
962 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700963 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800964 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700965 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800966
Allie Woodfb04d302015-04-03 14:25:48 -0700967 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Gilad Arnold5502b562013-03-08 13:22:31 -0800968 # Generate a test payload. For this test, we only care about one
969 # (arbitrary) set of operations, so we'll only be generating kernel and
970 # test with them.
971 payload_gen = test_utils.PayloadGenerator()
972
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700973 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800974 payload_gen.SetBlockSize(block_size)
975
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700976 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800977
978 # Fake rootfs operations in a full update, tampered with as required.
979 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -0800980 rootfs_data_length = rootfs_part_size
981 if fail_nonexhaustive_full_update:
982 rootfs_data_length -= block_size
983
984 payload_gen.AddOperation(False, rootfs_op_type,
985 dst_extents=[(0, rootfs_data_length / block_size)],
986 data_offset=0,
987 data_length=rootfs_data_length)
988
989 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700990 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
991 checker_init_dargs={
992 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -0800993 payload_checker.payload_type = checker._TYPE_FULL
994 report = checker._PayloadReport()
995
Amin Hassaniae853742017-10-11 10:27:27 -0700996 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
997 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -0700998 if fail_nonexhaustive_full_update:
Gilad Arnold5502b562013-03-08 13:22:31 -0800999 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001000 payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001001 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001002 self.assertEqual(rootfs_data_length,
1003 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001004
1005 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1006 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1007 fail_unknown_sig_version, fail_incorrect_sig):
1008 # Generate a test payload. For this test, we only care about the signature
1009 # block and how it relates to the payload hash. Therefore, we're generating
1010 # a random (otherwise useless) payload for this purpose.
1011 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001012 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001013 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001014 rootfs_part_size = test_utils.MiB(2)
1015 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001016 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1017 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001018 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001019 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001020 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001021 payload_gen.AddOperationWithData(
1022 False, common.OpType.REPLACE,
1023 dst_extents=[(0, rootfs_part_size / block_size)],
1024 data_blob=os.urandom(rootfs_part_size))
1025
1026 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1027 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1028 fail_sig_missing_fields or fail_unknown_sig_version
1029 or fail_incorrect_sig)
1030
1031 sigs_data = None
1032 if do_forge_sigs_data:
1033 sigs_gen = test_utils.SignaturesGenerator()
1034 if not fail_empty_sigs_blob:
1035 if fail_sig_missing_fields:
1036 sig_data = None
1037 else:
1038 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001039 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001040 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1041
1042 sigs_data = sigs_gen.ToBinary()
1043 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1044
1045 if do_forge_pseudo_op:
1046 assert sigs_data is not None, 'should have forged signatures blob by now'
1047 sigs_len = len(sigs_data)
1048 payload_gen.AddOperation(
1049 False, common.OpType.REPLACE,
1050 data_offset=payload_gen.curr_offset / 2,
1051 data_length=sigs_len / 2,
1052 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1053
1054 # Generate payload (complete w/ signature) and create the test object.
1055 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001056 payload_gen.WriteToFileWithData,
1057 payload_gen_dargs={
1058 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001059 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001060 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001061 payload_checker.payload_type = checker._TYPE_FULL
1062 report = checker._PayloadReport()
1063
1064 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001065 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001066
1067 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1068 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1069 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001070 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001071 if should_fail:
1072 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001073 payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001074 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001075 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001076
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001077 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1078 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001079
1080 Args:
1081 minor_version: The payload minor version to test with.
1082 payload_type: The type of the payload we're testing, delta or full.
1083 """
1084 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001085 payload = self.MockPayload()
1086 payload.manifest.minor_version = minor_version
1087 payload_checker = checker.PayloadChecker(payload)
1088 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001089 report = checker._PayloadReport()
1090
1091 should_succeed = (
1092 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1093 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001094 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001095 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1096 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001097 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001098
1099 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001100 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001101 else:
1102 self.assertRaises(update_payload.PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001103 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001104
Gilad Arnold06eea332015-07-13 18:06:33 -07001105 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1106 fail_wrong_payload_type, fail_invalid_block_size,
1107 fail_mismatched_block_size, fail_excess_data,
1108 fail_rootfs_part_size_exceeded,
1109 fail_kernel_part_size_exceeded):
Gilad Arnold5502b562013-03-08 13:22:31 -08001110 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001111 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001112 # internal PayloadChecker methods that are tested elsewhere, here we only
1113 # tamper with what's actually being manipulated and/or tested in the Run()
1114 # method itself. Note that the checker doesn't verify partition hashes, so
1115 # they're safe to fake.
1116 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001117 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001118 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001119 kernel_filesystem_size = test_utils.KiB(16)
1120 rootfs_filesystem_size = test_utils.MiB(2)
1121 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001122 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001123 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001124 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001125 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001126
1127 rootfs_part_size = 0
1128 if rootfs_part_size_provided:
1129 rootfs_part_size = rootfs_filesystem_size + block_size
1130 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1131 if fail_rootfs_part_size_exceeded:
1132 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001133 payload_gen.AddOperationWithData(
1134 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001135 dst_extents=[(0, rootfs_op_size / block_size)],
1136 data_blob=os.urandom(rootfs_op_size))
1137
1138 kernel_part_size = 0
1139 if kernel_part_size_provided:
1140 kernel_part_size = kernel_filesystem_size + block_size
1141 kernel_op_size = kernel_part_size or kernel_filesystem_size
1142 if fail_kernel_part_size_exceeded:
1143 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001144 payload_gen.AddOperationWithData(
1145 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001146 dst_extents=[(0, kernel_op_size / block_size)],
1147 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001148
1149 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001150 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001151 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001152 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001153 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001154 else:
1155 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001156
Gilad Arnoldcb638912013-06-24 04:57:11 -07001157 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001158 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001159 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001160 'do_add_pseudo_operation': True,
1161 'is_pseudo_in_kernel': True,
1162 'padding': os.urandom(1024) if fail_excess_data else None},
1163 'checker_init_dargs': {
1164 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1165 'block_size': use_block_size}}
1166 if fail_invalid_block_size:
1167 self.assertRaises(update_payload.PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001168 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001169 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001170 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001171 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001172
1173 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1174 'rootfs_part_size': rootfs_part_size,
1175 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001176 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001177 fail_excess_data or
1178 fail_rootfs_part_size_exceeded or
1179 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001180 if should_fail:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001181 self.assertRaises(update_payload.PayloadError, payload_checker.Run,
1182 **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001183 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001184 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001185
Gilad Arnold5502b562013-03-08 13:22:31 -08001186# This implements a generic API, hence the occasional unused args.
1187# pylint: disable=W0613
1188def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1189 allow_unhashed, fail_src_extents,
1190 fail_dst_extents,
1191 fail_mismatched_data_offset_length,
1192 fail_missing_dst_extents, fail_src_length,
1193 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001194 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001195 """Returns True iff the combination of arguments represents a valid test."""
1196 op_type = _OpTypeByName(op_type_name)
1197
Allie Wood7cf9f132015-02-26 14:28:19 -08001198 # REPLACE/REPLACE_BZ operations don't read data from src partition. They are
1199 # compatible with all valid minor versions, so we don't need to check that.
Gilad Arnold5502b562013-03-08 13:22:31 -08001200 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ) and (
Allie Wood7cf9f132015-02-26 14:28:19 -08001201 fail_src_extents or fail_src_length or fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001202 return False
1203
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001204 # MOVE and SOURCE_COPY operations don't carry data.
1205 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001206 fail_mismatched_data_offset_length or fail_data_hash or
1207 fail_prev_data_offset)):
1208 return False
1209
1210 return True
1211
1212
1213def TestMethodBody(run_method_name, run_dargs):
1214 """Returns a function that invokes a named method with named arguments."""
1215 return lambda self: getattr(self, run_method_name)(**run_dargs)
1216
1217
1218def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1219 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1220
1221 This function enumerates a space of test parameters (defined by arg_space),
1222 then binds a new, unique method name in PayloadCheckerTest to a test function
1223 that gets handed the said parameters. This is a preferable approach to doing
1224 the enumeration and invocation during the tests because this way each test is
1225 treated as a complete run by the unittest framework, and so benefits from the
1226 usual setUp/tearDown mechanics.
1227
1228 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001229 tested_method_name: Name of the tested PayloadChecker method.
1230 arg_space: A dictionary containing variables (keys) and lists of values
1231 (values) associated with them.
1232 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001233 """
1234 for value_tuple in itertools.product(*arg_space.itervalues()):
1235 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1236 if validate_func and not validate_func(**run_dargs):
1237 continue
1238 run_method_name = 'Do%sTest' % tested_method_name
1239 test_method_name = 'test%s' % tested_method_name
1240 for arg_key, arg_val in run_dargs.iteritems():
1241 if arg_val or type(arg_val) is int:
1242 test_method_name += '__%s=%s' % (arg_key, arg_val)
1243 setattr(PayloadCheckerTest, test_method_name,
1244 TestMethodBody(run_method_name, run_dargs))
1245
1246
1247def AddAllParametricTests():
1248 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1249 # Add all _CheckElem() test cases.
1250 AddParametricTests('AddElem',
1251 {'linebreak': (True, False),
1252 'indent': (0, 1, 2),
1253 'convert': (str, lambda s: s[::-1]),
1254 'is_present': (True, False),
1255 'is_mandatory': (True, False),
1256 'is_submsg': (True, False)})
1257
1258 # Add all _Add{Mandatory,Optional}Field tests.
1259 AddParametricTests('AddField',
1260 {'is_mandatory': (True, False),
1261 'linebreak': (True, False),
1262 'indent': (0, 1, 2),
1263 'convert': (str, lambda s: s[::-1]),
1264 'is_present': (True, False)})
1265
1266 # Add all _Add{Mandatory,Optional}SubMsg tests.
1267 AddParametricTests('AddSubMsg',
1268 {'is_mandatory': (True, False),
1269 'is_present': (True, False)})
1270
1271 # Add all _CheckManifest() test cases.
1272 AddParametricTests('CheckManifest',
1273 {'fail_mismatched_block_size': (True, False),
1274 'fail_bad_sigs': (True, False),
1275 'fail_mismatched_oki_ori': (True, False),
1276 'fail_bad_oki': (True, False),
1277 'fail_bad_ori': (True, False),
1278 'fail_bad_nki': (True, False),
1279 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001280 'fail_old_kernel_fs_size': (True, False),
1281 'fail_old_rootfs_fs_size': (True, False),
1282 'fail_new_kernel_fs_size': (True, False),
1283 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001284
1285 # Add all _CheckOperation() test cases.
1286 AddParametricTests('CheckOperation',
1287 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'MOVE',
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001288 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001289 'SOURCE_BSDIFF', 'PUFFDIFF',
1290 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001291 'is_last': (True, False),
1292 'allow_signature': (True, False),
1293 'allow_unhashed': (True, False),
1294 'fail_src_extents': (True, False),
1295 'fail_dst_extents': (True, False),
1296 'fail_mismatched_data_offset_length': (True, False),
1297 'fail_missing_dst_extents': (True, False),
1298 'fail_src_length': (True, False),
1299 'fail_dst_length': (True, False),
1300 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001301 'fail_prev_data_offset': (True, False),
1302 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001303 validate_func=ValidateCheckOperationTest)
1304
1305 # Add all _CheckOperations() test cases.
1306 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001307 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001308
1309 # Add all _CheckOperations() test cases.
1310 AddParametricTests('CheckSignatures',
1311 {'fail_empty_sigs_blob': (True, False),
1312 'fail_missing_pseudo_op': (True, False),
1313 'fail_mismatched_pseudo_op': (True, False),
1314 'fail_sig_missing_fields': (True, False),
1315 'fail_unknown_sig_version': (True, False),
1316 'fail_incorrect_sig': (True, False)})
1317
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001318 # Add all _CheckManifestMinorVersion() test cases.
1319 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001320 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001321 'payload_type': (checker._TYPE_FULL,
1322 checker._TYPE_DELTA)})
1323
Gilad Arnold5502b562013-03-08 13:22:31 -08001324 # Add all Run() test cases.
1325 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001326 {'rootfs_part_size_provided': (True, False),
1327 'kernel_part_size_provided': (True, False),
1328 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001329 'fail_invalid_block_size': (True, False),
1330 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001331 'fail_excess_data': (True, False),
1332 'fail_rootfs_part_size_exceeded': (True, False),
1333 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001334
1335
1336if __name__ == '__main__':
1337 AddAllParametricTests()
1338 unittest.main()