blob: a38e334a50d966c855b1823633cc605ebe2b61df [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
Amin Hassanif1d6cea2017-12-07 12:13:03 -0800637 def testCheckReplaceXzOperation(self):
638 """Tests _CheckReplaceOperation() where op.type == REPLACE_XZ."""
639 payload_checker = checker.PayloadChecker(self.MockPayload())
640 block_size = payload_checker.block_size
641 data_length = block_size * 3
642
643 op = self.mox.CreateMock(
644 update_metadata_pb2.InstallOperation)
645 op.type = common.OpType.REPLACE_XZ
646
647 # Pass.
648 op.src_extents = []
649 self.assertIsNone(
650 payload_checker._CheckReplaceOperation(
651 op, data_length, (data_length + block_size - 1) / block_size + 5,
652 'foo'))
653
654 # Fail, src extents founds.
655 op.src_extents = ['bar']
656 self.assertRaises(
657 update_payload.PayloadError,
658 payload_checker._CheckReplaceOperation,
659 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
660
661 # Fail, missing data.
662 op.src_extents = []
663 self.assertRaises(
664 update_payload.PayloadError,
665 payload_checker._CheckReplaceOperation,
666 op, None, (data_length + block_size - 1) / block_size, 'foo')
667
668 # Fail, too few blocks to justify XZ.
669 op.src_extents = []
670 self.assertRaises(
671 update_payload.PayloadError,
672 payload_checker._CheckReplaceOperation,
673 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
674
Gilad Arnold5502b562013-03-08 13:22:31 -0800675 def testCheckMoveOperation_Pass(self):
676 """Tests _CheckMoveOperation(); pass case."""
677 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700678 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800679 op.type = common.OpType.MOVE
680
681 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700682 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800683 self.AddToMessage(op.dst_extents,
684 self.NewExtentList((16, 128), (512, 6)))
685 self.assertIsNone(
686 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
687
688 def testCheckMoveOperation_FailContainsData(self):
689 """Tests _CheckMoveOperation(); fails, message contains data."""
690 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700691 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800692 op.type = common.OpType.MOVE
693
694 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700695 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800696 self.AddToMessage(op.dst_extents,
697 self.NewExtentList((16, 128), (512, 6)))
698 self.assertRaises(
699 update_payload.PayloadError,
700 payload_checker._CheckMoveOperation,
701 op, 1024, 134, 134, 'foo')
702
703 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
704 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
705 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700706 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800707 op.type = common.OpType.MOVE
708
709 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700710 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
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_FailInsufficientDstBlocks(self):
719 """Tests _CheckMoveOperation(); fails, not enough 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, 5)))
728 self.assertRaises(
729 update_payload.PayloadError,
730 payload_checker._CheckMoveOperation,
731 op, None, 134, 134, 'foo')
732
733 def testCheckMoveOperation_FailExcessSrcBlocks(self):
734 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
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)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800741 self.AddToMessage(op.dst_extents,
742 self.NewExtentList((16, 128), (512, 5)))
743 self.assertRaises(
744 update_payload.PayloadError,
745 payload_checker._CheckMoveOperation,
746 op, None, 134, 134, 'foo')
747 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700748 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800749 self.AddToMessage(op.dst_extents,
750 self.NewExtentList((16, 128), (512, 6)))
751 self.assertRaises(
752 update_payload.PayloadError,
753 payload_checker._CheckMoveOperation,
754 op, None, 134, 134, 'foo')
755
756 def testCheckMoveOperation_FailExcessDstBlocks(self):
757 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
758 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700759 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800760 op.type = common.OpType.MOVE
761
762 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700763 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800764 self.AddToMessage(op.dst_extents,
765 self.NewExtentList((16, 128), (512, 7)))
766 self.assertRaises(
767 update_payload.PayloadError,
768 payload_checker._CheckMoveOperation,
769 op, None, 134, 134, 'foo')
770
771 def testCheckMoveOperation_FailStagnantBlocks(self):
772 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
773 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700774 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800775 op.type = common.OpType.MOVE
776
777 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700778 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
779 self.AddToMessage(op.dst_extents,
780 self.NewExtentList((8, 128), (512, 6)))
781 self.assertRaises(
782 update_payload.PayloadError,
783 payload_checker._CheckMoveOperation,
784 op, None, 134, 134, 'foo')
785
786 def testCheckMoveOperation_FailZeroStartBlock(self):
787 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
788 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700789 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700790 op.type = common.OpType.MOVE
791
792 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800793 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
794 self.AddToMessage(op.dst_extents,
795 self.NewExtentList((8, 128), (512, 6)))
796 self.assertRaises(
797 update_payload.PayloadError,
798 payload_checker._CheckMoveOperation,
799 op, None, 134, 134, 'foo')
800
Allie Woodb065e132015-04-24 10:20:27 -0700801 self.AddToMessage(op.src_extents,
802 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
803 self.AddToMessage(op.dst_extents,
804 self.NewExtentList((0, 128), (512, 6)))
805 self.assertRaises(
806 update_payload.PayloadError,
807 payload_checker._CheckMoveOperation,
808 op, None, 134, 134, 'foo')
809
Sen Jiang92161a72016-06-28 16:09:38 -0700810 def testCheckAnyDiff(self):
811 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800812 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800813 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800814
815 # Pass.
816 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800817 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800818
819 # Fail, missing data blob.
820 self.assertRaises(
821 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700822 payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800823 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800824
825 # Fail, too big of a diff blob (unjustified).
826 self.assertRaises(
827 update_payload.PayloadError,
Sen Jiang92161a72016-06-28 16:09:38 -0700828 payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800829 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800830
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800831 def testCheckSourceCopyOperation_Pass(self):
832 """Tests _CheckSourceCopyOperation(); pass case."""
833 payload_checker = checker.PayloadChecker(self.MockPayload())
834 self.assertIsNone(
835 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
836
837 def testCheckSourceCopyOperation_FailContainsData(self):
838 """Tests _CheckSourceCopyOperation(); message contains data."""
839 payload_checker = checker.PayloadChecker(self.MockPayload())
840 self.assertRaises(update_payload.PayloadError,
841 payload_checker._CheckSourceCopyOperation,
842 134, 0, 0, 'foo')
843
844 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
845 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
846 payload_checker = checker.PayloadChecker(self.MockPayload())
847 self.assertRaises(update_payload.PayloadError,
848 payload_checker._CheckSourceCopyOperation,
849 None, 0, 1, 'foo')
850
Gilad Arnold5502b562013-03-08 13:22:31 -0800851 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
852 allow_unhashed, fail_src_extents, fail_dst_extents,
853 fail_mismatched_data_offset_length,
854 fail_missing_dst_extents, fail_src_length,
855 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800856 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800857 """Parametric testing of _CheckOperation().
858
859 Args:
Amin Hassanif1d6cea2017-12-07 12:13:03 -0800860 op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ', 'MOVE', 'BSDIFF',
861 'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700862 is_last: Whether we're testing the last operation in a sequence.
863 allow_signature: Whether we're testing a signature-capable operation.
864 allow_unhashed: Whether we're allowing to not hash the data.
865 fail_src_extents: Tamper with src extents.
866 fail_dst_extents: Tamper with dst extents.
867 fail_mismatched_data_offset_length: Make data_{offset,length}
868 inconsistent.
869 fail_missing_dst_extents: Do not include dst extents.
870 fail_src_length: Make src length inconsistent.
871 fail_dst_length: Make dst length inconsistent.
872 fail_data_hash: Tamper with the data blob hash.
873 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800874 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800875 """
876 op_type = _OpTypeByName(op_type_name)
877
878 # Create the test object.
879 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700880 payload_checker = checker.PayloadChecker(payload,
881 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800882 block_size = payload_checker.block_size
883
884 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700885 old_part_size = test_utils.MiB(4)
886 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800887 old_block_counters = array.array(
888 'B', [0] * ((old_part_size + block_size - 1) / block_size))
889 new_block_counters = array.array(
890 'B', [0] * ((new_part_size + block_size - 1) / block_size))
891 prev_data_offset = 1876
892 blob_hash_counts = collections.defaultdict(int)
893
894 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700895 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800896 op.type = op_type
897
898 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800899 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700900 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800901 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800902 if fail_src_extents:
903 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700904 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800905 else:
906 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700907 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800908 total_src_blocks = 16
909
Allie Wood7cf9f132015-02-26 14:28:19 -0800910 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700911 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800912 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700913 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800914 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700915 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassanif1d6cea2017-12-07 12:13:03 -0800916 if op_type == common.OpType.REPLACE_XZ:
917 payload_checker.minor_version = 2 if fail_bad_minor_version else 3
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700918 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800919 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700920 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800921
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800922 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800923 if not fail_mismatched_data_offset_length:
924 op.data_length = 16 * block_size - 8
925 if fail_prev_data_offset:
926 op.data_offset = prev_data_offset + 16
927 else:
928 op.data_offset = prev_data_offset
929
930 fake_data = 'fake-data'.ljust(op.data_length)
931 if not (allow_unhashed or (is_last and allow_signature and
932 op_type == common.OpType.REPLACE)):
933 if not fail_data_hash:
934 # Create a valid data blob hash.
935 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
936 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
937 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800938
Gilad Arnold5502b562013-03-08 13:22:31 -0800939 elif fail_data_hash:
940 # Create an invalid data blob hash.
941 op.data_sha256_hash = hashlib.sha256(
942 fake_data.replace(' ', '-')).digest()
943 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
944 fake_data)
945
946 total_dst_blocks = 0
947 if not fail_missing_dst_extents:
948 total_dst_blocks = 16
949 if fail_dst_extents:
950 self.AddToMessage(op.dst_extents,
951 self.NewExtentList((4, 16), (32, 0)))
952 else:
953 self.AddToMessage(op.dst_extents,
954 self.NewExtentList((4, 8), (64, 8)))
955
956 if total_src_blocks:
957 if fail_src_length:
958 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800959 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
960 common.OpType.SOURCE_BSDIFF) and
961 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800962 op.src_length = total_src_blocks * block_size
963 elif fail_src_length:
964 # Add an orphaned src_length.
965 op.src_length = 16
966
967 if total_dst_blocks:
968 if fail_dst_length:
969 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800970 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
971 common.OpType.SOURCE_BSDIFF) and
972 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800973 op.dst_length = total_dst_blocks * block_size
974
975 self.mox.ReplayAll()
976 should_fail = (fail_src_extents or fail_dst_extents or
977 fail_mismatched_data_offset_length or
978 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800979 fail_dst_length or fail_data_hash or fail_prev_data_offset or
980 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700981 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
982 old_part_size, new_part_size, prev_data_offset, allow_signature,
983 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800984 if should_fail:
985 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700986 payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800987 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700988 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
989 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800990
991 def testAllocBlockCounters(self):
992 """Tests _CheckMoveOperation()."""
993 payload_checker = checker.PayloadChecker(self.MockPayload())
994 block_size = payload_checker.block_size
995
996 # Check allocation for block-aligned partition size, ensure it's integers.
997 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700998 self.assertEqual(16, len(result))
999 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -08001000
1001 # Check allocation of unaligned partition sizes.
1002 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001003 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -08001004 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001005 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -08001006
Allie Woodfb04d302015-04-03 14:25:48 -07001007 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Gilad Arnold5502b562013-03-08 13:22:31 -08001008 # Generate a test payload. For this test, we only care about one
1009 # (arbitrary) set of operations, so we'll only be generating kernel and
1010 # test with them.
1011 payload_gen = test_utils.PayloadGenerator()
1012
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001013 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001014 payload_gen.SetBlockSize(block_size)
1015
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001016 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -08001017
1018 # Fake rootfs operations in a full update, tampered with as required.
1019 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -08001020 rootfs_data_length = rootfs_part_size
1021 if fail_nonexhaustive_full_update:
1022 rootfs_data_length -= block_size
1023
1024 payload_gen.AddOperation(False, rootfs_op_type,
1025 dst_extents=[(0, rootfs_data_length / block_size)],
1026 data_offset=0,
1027 data_length=rootfs_data_length)
1028
1029 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001030 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
1031 checker_init_dargs={
1032 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -08001033 payload_checker.payload_type = checker._TYPE_FULL
1034 report = checker._PayloadReport()
1035
Amin Hassaniae853742017-10-11 10:27:27 -07001036 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
1037 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -07001038 if fail_nonexhaustive_full_update:
Gilad Arnold5502b562013-03-08 13:22:31 -08001039 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001040 payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001041 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001042 self.assertEqual(rootfs_data_length,
1043 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001044
1045 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
1046 fail_mismatched_pseudo_op, fail_sig_missing_fields,
1047 fail_unknown_sig_version, fail_incorrect_sig):
1048 # Generate a test payload. For this test, we only care about the signature
1049 # block and how it relates to the payload hash. Therefore, we're generating
1050 # a random (otherwise useless) payload for this purpose.
1051 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001052 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001053 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001054 rootfs_part_size = test_utils.MiB(2)
1055 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -08001056 payload_gen.SetPartInfo(False, True, rootfs_part_size,
1057 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -07001058 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001059 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001060 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -08001061 payload_gen.AddOperationWithData(
1062 False, common.OpType.REPLACE,
1063 dst_extents=[(0, rootfs_part_size / block_size)],
1064 data_blob=os.urandom(rootfs_part_size))
1065
1066 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1067 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1068 fail_sig_missing_fields or fail_unknown_sig_version
1069 or fail_incorrect_sig)
1070
1071 sigs_data = None
1072 if do_forge_sigs_data:
1073 sigs_gen = test_utils.SignaturesGenerator()
1074 if not fail_empty_sigs_blob:
1075 if fail_sig_missing_fields:
1076 sig_data = None
1077 else:
1078 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001079 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001080 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1081
1082 sigs_data = sigs_gen.ToBinary()
1083 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1084
1085 if do_forge_pseudo_op:
1086 assert sigs_data is not None, 'should have forged signatures blob by now'
1087 sigs_len = len(sigs_data)
1088 payload_gen.AddOperation(
1089 False, common.OpType.REPLACE,
1090 data_offset=payload_gen.curr_offset / 2,
1091 data_length=sigs_len / 2,
1092 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1093
1094 # Generate payload (complete w/ signature) and create the test object.
1095 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001096 payload_gen.WriteToFileWithData,
1097 payload_gen_dargs={
1098 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001099 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001100 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001101 payload_checker.payload_type = checker._TYPE_FULL
1102 report = checker._PayloadReport()
1103
1104 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001105 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001106
1107 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1108 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1109 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001110 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001111 if should_fail:
1112 self.assertRaises(update_payload.PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001113 payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001114 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001115 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001116
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001117 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1118 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001119
1120 Args:
1121 minor_version: The payload minor version to test with.
1122 payload_type: The type of the payload we're testing, delta or full.
1123 """
1124 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001125 payload = self.MockPayload()
1126 payload.manifest.minor_version = minor_version
1127 payload_checker = checker.PayloadChecker(payload)
1128 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001129 report = checker._PayloadReport()
1130
1131 should_succeed = (
1132 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1133 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001134 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001135 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1136 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001137 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001138
1139 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001140 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001141 else:
1142 self.assertRaises(update_payload.PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001143 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001144
Gilad Arnold06eea332015-07-13 18:06:33 -07001145 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1146 fail_wrong_payload_type, fail_invalid_block_size,
1147 fail_mismatched_block_size, fail_excess_data,
1148 fail_rootfs_part_size_exceeded,
1149 fail_kernel_part_size_exceeded):
Gilad Arnold5502b562013-03-08 13:22:31 -08001150 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001151 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001152 # internal PayloadChecker methods that are tested elsewhere, here we only
1153 # tamper with what's actually being manipulated and/or tested in the Run()
1154 # method itself. Note that the checker doesn't verify partition hashes, so
1155 # they're safe to fake.
1156 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001157 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001158 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001159 kernel_filesystem_size = test_utils.KiB(16)
1160 rootfs_filesystem_size = test_utils.MiB(2)
1161 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001162 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001163 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001164 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001165 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001166
1167 rootfs_part_size = 0
1168 if rootfs_part_size_provided:
1169 rootfs_part_size = rootfs_filesystem_size + block_size
1170 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1171 if fail_rootfs_part_size_exceeded:
1172 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001173 payload_gen.AddOperationWithData(
1174 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001175 dst_extents=[(0, rootfs_op_size / block_size)],
1176 data_blob=os.urandom(rootfs_op_size))
1177
1178 kernel_part_size = 0
1179 if kernel_part_size_provided:
1180 kernel_part_size = kernel_filesystem_size + block_size
1181 kernel_op_size = kernel_part_size or kernel_filesystem_size
1182 if fail_kernel_part_size_exceeded:
1183 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001184 payload_gen.AddOperationWithData(
1185 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001186 dst_extents=[(0, kernel_op_size / block_size)],
1187 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001188
1189 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001190 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001191 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001192 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001193 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001194 else:
1195 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001196
Gilad Arnoldcb638912013-06-24 04:57:11 -07001197 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001198 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001199 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001200 'do_add_pseudo_operation': True,
1201 'is_pseudo_in_kernel': True,
1202 'padding': os.urandom(1024) if fail_excess_data else None},
1203 'checker_init_dargs': {
1204 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1205 'block_size': use_block_size}}
1206 if fail_invalid_block_size:
1207 self.assertRaises(update_payload.PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001208 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001209 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001210 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001211 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001212
1213 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1214 'rootfs_part_size': rootfs_part_size,
1215 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001216 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001217 fail_excess_data or
1218 fail_rootfs_part_size_exceeded or
1219 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001220 if should_fail:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001221 self.assertRaises(update_payload.PayloadError, payload_checker.Run,
1222 **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001223 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001224 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001225
Gilad Arnold5502b562013-03-08 13:22:31 -08001226# This implements a generic API, hence the occasional unused args.
1227# pylint: disable=W0613
1228def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1229 allow_unhashed, fail_src_extents,
1230 fail_dst_extents,
1231 fail_mismatched_data_offset_length,
1232 fail_missing_dst_extents, fail_src_length,
1233 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001234 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001235 """Returns True iff the combination of arguments represents a valid test."""
1236 op_type = _OpTypeByName(op_type_name)
1237
Amin Hassanif1d6cea2017-12-07 12:13:03 -08001238 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1239 # partition. They are compatible with all valid minor versions, so we don't
1240 # need to check that.
1241 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1242 common.OpType.REPLACE_XZ) and (fail_src_extents or
1243 fail_src_length or
1244 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001245 return False
1246
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001247 # MOVE and SOURCE_COPY operations don't carry data.
1248 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001249 fail_mismatched_data_offset_length or fail_data_hash or
1250 fail_prev_data_offset)):
1251 return False
1252
1253 return True
1254
1255
1256def TestMethodBody(run_method_name, run_dargs):
1257 """Returns a function that invokes a named method with named arguments."""
1258 return lambda self: getattr(self, run_method_name)(**run_dargs)
1259
1260
1261def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1262 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1263
1264 This function enumerates a space of test parameters (defined by arg_space),
1265 then binds a new, unique method name in PayloadCheckerTest to a test function
1266 that gets handed the said parameters. This is a preferable approach to doing
1267 the enumeration and invocation during the tests because this way each test is
1268 treated as a complete run by the unittest framework, and so benefits from the
1269 usual setUp/tearDown mechanics.
1270
1271 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001272 tested_method_name: Name of the tested PayloadChecker method.
1273 arg_space: A dictionary containing variables (keys) and lists of values
1274 (values) associated with them.
1275 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001276 """
1277 for value_tuple in itertools.product(*arg_space.itervalues()):
1278 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1279 if validate_func and not validate_func(**run_dargs):
1280 continue
1281 run_method_name = 'Do%sTest' % tested_method_name
1282 test_method_name = 'test%s' % tested_method_name
1283 for arg_key, arg_val in run_dargs.iteritems():
1284 if arg_val or type(arg_val) is int:
1285 test_method_name += '__%s=%s' % (arg_key, arg_val)
1286 setattr(PayloadCheckerTest, test_method_name,
1287 TestMethodBody(run_method_name, run_dargs))
1288
1289
1290def AddAllParametricTests():
1291 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1292 # Add all _CheckElem() test cases.
1293 AddParametricTests('AddElem',
1294 {'linebreak': (True, False),
1295 'indent': (0, 1, 2),
1296 'convert': (str, lambda s: s[::-1]),
1297 'is_present': (True, False),
1298 'is_mandatory': (True, False),
1299 'is_submsg': (True, False)})
1300
1301 # Add all _Add{Mandatory,Optional}Field tests.
1302 AddParametricTests('AddField',
1303 {'is_mandatory': (True, False),
1304 'linebreak': (True, False),
1305 'indent': (0, 1, 2),
1306 'convert': (str, lambda s: s[::-1]),
1307 'is_present': (True, False)})
1308
1309 # Add all _Add{Mandatory,Optional}SubMsg tests.
1310 AddParametricTests('AddSubMsg',
1311 {'is_mandatory': (True, False),
1312 'is_present': (True, False)})
1313
1314 # Add all _CheckManifest() test cases.
1315 AddParametricTests('CheckManifest',
1316 {'fail_mismatched_block_size': (True, False),
1317 'fail_bad_sigs': (True, False),
1318 'fail_mismatched_oki_ori': (True, False),
1319 'fail_bad_oki': (True, False),
1320 'fail_bad_ori': (True, False),
1321 'fail_bad_nki': (True, False),
1322 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001323 'fail_old_kernel_fs_size': (True, False),
1324 'fail_old_rootfs_fs_size': (True, False),
1325 'fail_new_kernel_fs_size': (True, False),
1326 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001327
1328 # Add all _CheckOperation() test cases.
1329 AddParametricTests('CheckOperation',
Amin Hassanif1d6cea2017-12-07 12:13:03 -08001330 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
1331 'MOVE', 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001332 'SOURCE_BSDIFF', 'PUFFDIFF',
1333 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001334 'is_last': (True, False),
1335 'allow_signature': (True, False),
1336 'allow_unhashed': (True, False),
1337 'fail_src_extents': (True, False),
1338 'fail_dst_extents': (True, False),
1339 'fail_mismatched_data_offset_length': (True, False),
1340 'fail_missing_dst_extents': (True, False),
1341 'fail_src_length': (True, False),
1342 'fail_dst_length': (True, False),
1343 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001344 'fail_prev_data_offset': (True, False),
1345 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001346 validate_func=ValidateCheckOperationTest)
1347
1348 # Add all _CheckOperations() test cases.
1349 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001350 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001351
1352 # Add all _CheckOperations() test cases.
1353 AddParametricTests('CheckSignatures',
1354 {'fail_empty_sigs_blob': (True, False),
1355 'fail_missing_pseudo_op': (True, False),
1356 'fail_mismatched_pseudo_op': (True, False),
1357 'fail_sig_missing_fields': (True, False),
1358 'fail_unknown_sig_version': (True, False),
1359 'fail_incorrect_sig': (True, False)})
1360
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001361 # Add all _CheckManifestMinorVersion() test cases.
1362 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001363 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001364 'payload_type': (checker._TYPE_FULL,
1365 checker._TYPE_DELTA)})
1366
Gilad Arnold5502b562013-03-08 13:22:31 -08001367 # Add all Run() test cases.
1368 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001369 {'rootfs_part_size_provided': (True, False),
1370 'kernel_part_size_provided': (True, False),
1371 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001372 'fail_invalid_block_size': (True, False),
1373 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001374 'fail_excess_data': (True, False),
1375 'fail_rootfs_part_size_exceeded': (True, False),
1376 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001377
1378
1379if __name__ == '__main__':
1380 AddAllParametricTests()
1381 unittest.main()