blob: b5f2f3e262bfcc6082a6ca483222f4144b275947 [file] [log] [blame]
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001#!/usr/bin/python2
Gilad Arnold5502b562013-03-08 13:22:31 -08002#
Amin Hassanif94b6432018-01-26 17:39:47 -08003# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Gilad Arnold5502b562013-03-08 13:22:31 -080017
18"""Unit testing checker.py."""
19
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080020from __future__ import print_function
21
Gilad Arnold5502b562013-03-08 13:22:31 -080022import array
23import collections
24import cStringIO
25import hashlib
26import itertools
27import os
28import unittest
29
Gilad Arnoldcb638912013-06-24 04:57:11 -070030# pylint cannot find mox.
Gilad Arnold5502b562013-03-08 13:22:31 -080031# pylint: disable=F0401
32import mox
33
Amin Hassanib05a65a2017-12-18 15:15:32 -080034from update_payload import checker
35from update_payload import common
36from update_payload import test_utils
37from update_payload import update_metadata_pb2
38from update_payload.error import PayloadError
39from update_payload.payload import Payload # Avoid name conflicts later.
Gilad Arnold5502b562013-03-08 13:22:31 -080040
41
Gilad Arnold5502b562013-03-08 13:22:31 -080042def _OpTypeByName(op_name):
Amin Hassanib05a65a2017-12-18 15:15:32 -080043 """Returns the type of an operation from itsname."""
Gilad Arnold5502b562013-03-08 13:22:31 -080044 op_name_to_type = {
45 'REPLACE': common.OpType.REPLACE,
46 'REPLACE_BZ': common.OpType.REPLACE_BZ,
Allie Woodf5c4f3e2015-02-20 16:57:46 -080047 'SOURCE_COPY': common.OpType.SOURCE_COPY,
48 'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
Alex Deymo28466772015-09-11 17:16:44 -070049 'ZERO': common.OpType.ZERO,
50 'DISCARD': common.OpType.DISCARD,
51 'REPLACE_XZ': common.OpType.REPLACE_XZ,
Amin Hassani5ef5d452017-08-04 13:10:59 -070052 'PUFFDIFF': common.OpType.PUFFDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -080053 'BROTLI_BSDIFF': common.OpType.BROTLI_BSDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080054 }
55 return op_name_to_type[op_name]
56
57
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070058def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
59 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080060 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070061 if payload_gen_dargs is None:
62 payload_gen_dargs = {}
63 if checker_init_dargs is None:
64 checker_init_dargs = {}
65
Gilad Arnold5502b562013-03-08 13:22:31 -080066 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070067 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080068 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080069 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080070 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070071 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080072
73
74def _GetPayloadCheckerWithData(payload_gen):
75 """Returns a payload checker from a given payload generator."""
76 payload_file = cStringIO.StringIO()
77 payload_gen.WriteToFile(payload_file)
78 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080079 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080080 payload.Init()
81 return checker.PayloadChecker(payload)
82
83
Gilad Arnoldcb638912013-06-24 04:57:11 -070084# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080085# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070086# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080087# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070088# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080089# pylint: disable=E1101
90class PayloadCheckerTest(mox.MoxTestBase):
91 """Tests the PayloadChecker class.
92
93 In addition to ordinary testFoo() methods, which are automatically invoked by
94 the unittest framework, in this class we make use of DoBarTest() calls that
95 implement parametric tests of certain features. In order to invoke each test,
96 which embodies a unique combination of parameter values, as a complete unit
97 test, we perform explicit enumeration of the parameter space and create
98 individual invocation contexts for each, which are then bound as
99 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
100 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -0800101 """
102
103 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800104 """Create a mock payload object, complete with a mock manifest."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800105 payload = self.mox.CreateMock(Payload)
Gilad Arnold5502b562013-03-08 13:22:31 -0800106 payload.is_init = True
107 payload.manifest = self.mox.CreateMock(
108 update_metadata_pb2.DeltaArchiveManifest)
109 return payload
110
111 @staticmethod
112 def NewExtent(start_block, num_blocks):
113 """Returns an Extent message.
114
115 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
116 its default state.
117
118 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700119 start_block: The starting block of the extent.
120 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800121
Gilad Arnold5502b562013-03-08 13:22:31 -0800122 Returns:
123 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800124 """
125 ex = update_metadata_pb2.Extent()
126 if start_block >= 0:
127 ex.start_block = start_block
128 if num_blocks >= 0:
129 ex.num_blocks = num_blocks
130 return ex
131
132 @staticmethod
133 def NewExtentList(*args):
134 """Returns an list of extents.
135
136 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700137 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800138
Gilad Arnold5502b562013-03-08 13:22:31 -0800139 Returns:
140 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800141 """
142 ex_list = []
143 for start_block, num_blocks in args:
144 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
145 return ex_list
146
147 @staticmethod
148 def AddToMessage(repeated_field, field_vals):
149 for field_val in field_vals:
150 new_field = repeated_field.add()
151 new_field.CopyFrom(field_val)
152
Gilad Arnold5502b562013-03-08 13:22:31 -0800153 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
154 linebreak=False, indent=0):
155 """Setup for testing of _CheckElem() and its derivatives.
156
157 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700158 is_present: Whether or not the element is found in the message.
159 is_submsg: Whether the element is a sub-message itself.
160 convert: A representation conversion function.
161 linebreak: Whether or not a linebreak is to be used in the report.
162 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800163
Gilad Arnold5502b562013-03-08 13:22:31 -0800164 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700165 msg: A mock message object.
166 report: A mock report object.
167 subreport: A mock sub-report object.
168 name: An element name to check.
169 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800170 """
171 name = 'foo'
172 val = 'fake submsg' if is_submsg else 'fake field'
173 subreport = 'fake subreport'
174
175 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700176 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800177 msg.HasField(name).AndReturn(is_present)
178 setattr(msg, name, val)
179
180 # Create a mock report.
181 report = self.mox.CreateMock(checker._PayloadReport)
182 if is_present:
183 if is_submsg:
184 report.AddSubReport(name).AndReturn(subreport)
185 else:
186 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
187
188 self.mox.ReplayAll()
189 return (msg, report, subreport, name, val)
190
191 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
192 linebreak, indent):
193 """Parametric testing of _CheckElem().
194
195 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700196 is_present: Whether or not the element is found in the message.
197 is_mandatory: Whether or not it's a mandatory element.
198 is_submsg: Whether the element is a sub-message itself.
199 convert: A representation conversion function.
200 linebreak: Whether or not a linebreak is to be used in the report.
201 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800202 """
203 msg, report, subreport, name, val = self.SetupAddElemTest(
204 is_present, is_submsg, convert, linebreak, indent)
205
Gilad Arnoldcb638912013-06-24 04:57:11 -0700206 args = (msg, name, report, is_mandatory, is_submsg)
207 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800208 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800209 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700210 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800211 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700212 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
213 **kwargs)
214 self.assertEquals(val if is_present else None, ret_val)
215 self.assertEquals(subreport if is_present and is_submsg else None,
216 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800217
218 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
219 indent):
220 """Parametric testing of _Check{Mandatory,Optional}Field().
221
222 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700223 is_mandatory: Whether we're testing a mandatory call.
224 is_present: Whether or not the element is found in the message.
225 convert: A representation conversion function.
226 linebreak: Whether or not a linebreak is to be used in the report.
227 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800228 """
229 msg, report, _, name, val = self.SetupAddElemTest(
230 is_present, False, convert, linebreak, indent)
231
232 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700233 args = [msg, name, report]
234 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800235 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700236 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800237 tested_func = checker.PayloadChecker._CheckMandatoryField
238 else:
239 tested_func = checker.PayloadChecker._CheckOptionalField
240
241 # Test the method call.
242 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800243 self.assertRaises(PayloadError, tested_func, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800244 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700245 ret_val = tested_func(*args, **kwargs)
246 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800247
248 def DoAddSubMsgTest(self, is_mandatory, is_present):
249 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
250
251 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700252 is_mandatory: Whether we're testing a mandatory call.
253 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800254 """
255 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
256
257 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700258 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800259 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700260 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800261 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
262 else:
263 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
264
265 # Test the method call.
266 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800267 self.assertRaises(PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800268 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700269 ret_val, ret_subreport = tested_func(*args)
270 self.assertEquals(val if is_present else None, ret_val)
271 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800272
273 def testCheckPresentIff(self):
274 """Tests _CheckPresentIff()."""
275 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
276 None, None, 'foo', 'bar', 'baz'))
277 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
278 'a', 'b', 'foo', 'bar', 'baz'))
Amin Hassanib05a65a2017-12-18 15:15:32 -0800279 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800280 'a', None, 'foo', 'bar', 'baz')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800281 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800282 None, 'b', 'foo', 'bar', 'baz')
283
284 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
285 sig_data, sig_asn1_header,
286 returned_signed_hash, expected_signed_hash):
287 """Parametric testing of _CheckSha256SignatureTest().
288
289 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700290 expect_pass: Whether or not it should pass.
291 expect_subprocess_call: Whether to expect the openssl call to happen.
292 sig_data: The signature raw data.
293 sig_asn1_header: The ASN1 header.
294 returned_signed_hash: The signed hash data retuned by openssl.
295 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800296 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700297 try:
298 # Stub out the subprocess invocation.
299 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
300 if expect_subprocess_call:
301 checker.PayloadChecker._Run(
302 mox.IsA(list), send_data=sig_data).AndReturn(
303 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800304
Gilad Arnoldcb638912013-06-24 04:57:11 -0700305 self.mox.ReplayAll()
306 if expect_pass:
307 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
308 sig_data, 'foo', expected_signed_hash, 'bar'))
309 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800310 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700311 checker.PayloadChecker._CheckSha256Signature,
312 sig_data, 'foo', expected_signed_hash, 'bar')
313 finally:
314 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800315
316 def testCheckSha256Signature_Pass(self):
317 """Tests _CheckSha256Signature(); pass case."""
318 sig_data = 'fake-signature'.ljust(256)
319 signed_hash = hashlib.sha256('fake-data').digest()
320 self.DoCheckSha256SignatureTest(True, True, sig_data,
321 common.SIG_ASN1_HEADER, signed_hash,
322 signed_hash)
323
324 def testCheckSha256Signature_FailBadSignature(self):
325 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700326 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800327 signed_hash = hashlib.sha256('fake-data').digest()
328 self.DoCheckSha256SignatureTest(False, False, sig_data,
329 common.SIG_ASN1_HEADER, signed_hash,
330 signed_hash)
331
332 def testCheckSha256Signature_FailBadOutputLength(self):
333 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
334 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700335 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800336 self.DoCheckSha256SignatureTest(False, True, sig_data,
337 common.SIG_ASN1_HEADER, signed_hash,
338 signed_hash)
339
340 def testCheckSha256Signature_FailBadAsnHeader(self):
341 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
342 sig_data = 'fake-signature'.ljust(256)
343 signed_hash = hashlib.sha256('fake-data').digest()
344 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
345 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
346 signed_hash, signed_hash)
347
348 def testCheckSha256Signature_FailBadHash(self):
349 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
350 sig_data = 'fake-signature'.ljust(256)
351 expected_signed_hash = hashlib.sha256('fake-data').digest()
352 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
353 self.DoCheckSha256SignatureTest(False, True, sig_data,
354 common.SIG_ASN1_HEADER,
355 expected_signed_hash, returned_signed_hash)
356
357 def testCheckBlocksFitLength_Pass(self):
358 """Tests _CheckBlocksFitLength(); pass case."""
359 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
360 64, 4, 16, 'foo'))
361 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
362 60, 4, 16, 'foo'))
363 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
364 49, 4, 16, 'foo'))
365 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
366 48, 3, 16, 'foo'))
367
368 def testCheckBlocksFitLength_TooManyBlocks(self):
369 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800370 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800371 checker.PayloadChecker._CheckBlocksFitLength,
372 64, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800373 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800374 checker.PayloadChecker._CheckBlocksFitLength,
375 60, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800376 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800377 checker.PayloadChecker._CheckBlocksFitLength,
378 49, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800379 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800380 checker.PayloadChecker._CheckBlocksFitLength,
381 48, 4, 16, 'foo')
382
383 def testCheckBlocksFitLength_TooFewBlocks(self):
384 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800385 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800386 checker.PayloadChecker._CheckBlocksFitLength,
387 64, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800388 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800389 checker.PayloadChecker._CheckBlocksFitLength,
390 60, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800391 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800392 checker.PayloadChecker._CheckBlocksFitLength,
393 49, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800394 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800395 checker.PayloadChecker._CheckBlocksFitLength,
396 48, 2, 16, 'foo')
397
398 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
399 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800400 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
401 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
402 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800403 """Parametric testing of _CheckManifest().
404
405 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700406 fail_mismatched_block_size: Simulate a missing block_size field.
407 fail_bad_sigs: Make signatures descriptor inconsistent.
408 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
409 fail_bad_oki: Tamper with old kernel info.
410 fail_bad_ori: Tamper with old rootfs info.
411 fail_bad_nki: Tamper with new kernel info.
412 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700413 fail_old_kernel_fs_size: Make old kernel fs size too big.
414 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
415 fail_new_kernel_fs_size: Make new kernel fs size too big.
416 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800417 """
418 # Generate a test payload. For this test, we only care about the manifest
419 # and don't need any data blobs, hence we can use a plain paylaod generator
420 # (which also gives us more control on things that can be screwed up).
421 payload_gen = test_utils.PayloadGenerator()
422
423 # Tamper with block size, if required.
424 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700425 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800426 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700427 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800428
429 # Add some operations.
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700430 payload_gen.AddOperation(False, common.OpType.SOURCE_COPY,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800431 src_extents=[(0, 16), (16, 497)],
432 dst_extents=[(16, 496), (0, 16)])
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700433 payload_gen.AddOperation(True, common.OpType.SOURCE_COPY,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800434 src_extents=[(0, 8), (8, 8)],
435 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800436
437 # Set an invalid signatures block (offset but no size), if required.
438 if fail_bad_sigs:
439 payload_gen.SetSignatures(32, None)
440
Gilad Arnold382df5c2013-05-03 12:49:28 -0700441 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700442 rootfs_part_size = test_utils.MiB(8)
443 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700444 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
445 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
446 if fail_old_kernel_fs_size:
447 old_kernel_fs_size += 100
448 if fail_old_rootfs_fs_size:
449 old_rootfs_fs_size += 100
450 if fail_new_kernel_fs_size:
451 new_kernel_fs_size += 100
452 if fail_new_rootfs_fs_size:
453 new_rootfs_fs_size += 100
454
Gilad Arnold5502b562013-03-08 13:22:31 -0800455 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700456 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800457 oki_hash = (None if fail_bad_oki
458 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700459 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
460 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
461 fail_bad_ori):
462 ori_hash = (None if fail_bad_ori
463 else hashlib.sha256('fake-ori-content').digest())
464 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800465
466 # Add new kernel/rootfs partition info.
467 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700468 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800469 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
470 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700471 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800472 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
473
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700474 # Set the minor version.
475 payload_gen.SetMinorVersion(0)
476
Gilad Arnold5502b562013-03-08 13:22:31 -0800477 # Create the test object.
478 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
479 report = checker._PayloadReport()
480
481 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
482 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800483 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
484 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
485 fail_new_rootfs_fs_size)
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700486 part_sizes = {
487 common.ROOTFS: rootfs_part_size,
488 common.KERNEL: kernel_part_size
489 }
490
Gilad Arnold5502b562013-03-08 13:22:31 -0800491 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800492 self.assertRaises(PayloadError, payload_checker._CheckManifest, report,
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700493 part_sizes)
Gilad Arnold5502b562013-03-08 13:22:31 -0800494 else:
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700495 self.assertIsNone(payload_checker._CheckManifest(report, part_sizes))
Gilad Arnold5502b562013-03-08 13:22:31 -0800496
497 def testCheckLength(self):
498 """Tests _CheckLength()."""
499 payload_checker = checker.PayloadChecker(self.MockPayload())
500 block_size = payload_checker.block_size
501
502 # Passes.
503 self.assertIsNone(payload_checker._CheckLength(
504 int(3.5 * block_size), 4, 'foo', 'bar'))
505 # Fails, too few blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800506 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800507 int(3.5 * block_size), 3, 'foo', 'bar')
508 # Fails, too many blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800509 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800510 int(3.5 * block_size), 5, 'foo', 'bar')
511
512 def testCheckExtents(self):
513 """Tests _CheckExtents()."""
514 payload_checker = checker.PayloadChecker(self.MockPayload())
515 block_size = payload_checker.block_size
516
517 # Passes w/ all real extents.
518 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
519 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700520 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800521 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700522 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800523
524 # Passes w/ pseudo-extents (aka sparse holes).
525 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
526 (8, 3))
527 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700528 12,
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_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800532
533 # Passes w/ pseudo-extent due to a signature.
534 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
535 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700536 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800537 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
538 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700539 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800540
541 # Fails, extent missing a start block.
542 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
543 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800544 PayloadError, payload_checker._CheckExtents, extents,
545 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800546
547 # Fails, extent missing block count.
548 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
549 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800550 PayloadError, payload_checker._CheckExtents, extents,
551 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800552
553 # Fails, extent has zero blocks.
554 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
555 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800556 PayloadError, payload_checker._CheckExtents, extents,
557 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800558
559 # Fails, extent exceeds partition boundaries.
560 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
561 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800562 PayloadError, payload_checker._CheckExtents, extents,
563 (1024 + 15) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800564
565 def testCheckReplaceOperation(self):
566 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
567 payload_checker = checker.PayloadChecker(self.MockPayload())
568 block_size = payload_checker.block_size
569 data_length = 10000
570
571 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700572 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800573 op.type = common.OpType.REPLACE
574
575 # Pass.
576 op.src_extents = []
577 self.assertIsNone(
578 payload_checker._CheckReplaceOperation(
579 op, data_length, (data_length + block_size - 1) / block_size,
580 'foo'))
581
582 # Fail, src extents founds.
583 op.src_extents = ['bar']
584 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800585 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800586 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
587
588 # Fail, missing data.
589 op.src_extents = []
590 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800591 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800592 op, None, (data_length + block_size - 1) / block_size, 'foo')
593
594 # Fail, length / block number mismatch.
595 op.src_extents = ['bar']
596 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800597 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800598 op, data_length, (data_length + block_size - 1) / block_size + 1, 'foo')
599
600 def testCheckReplaceBzOperation(self):
601 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
602 payload_checker = checker.PayloadChecker(self.MockPayload())
603 block_size = payload_checker.block_size
604 data_length = block_size * 3
605
606 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700607 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800608 op.type = common.OpType.REPLACE_BZ
609
610 # Pass.
611 op.src_extents = []
612 self.assertIsNone(
613 payload_checker._CheckReplaceOperation(
614 op, data_length, (data_length + block_size - 1) / block_size + 5,
615 'foo'))
616
617 # Fail, src extents founds.
618 op.src_extents = ['bar']
619 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800620 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800621 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
622
623 # Fail, missing data.
624 op.src_extents = []
625 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800626 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800627 op, None, (data_length + block_size - 1) / block_size, 'foo')
628
629 # Fail, too few blocks to justify BZ.
630 op.src_extents = []
631 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800632 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800633 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
634
Amin Hassani0de7f782017-12-07 12:13:03 -0800635 def testCheckReplaceXzOperation(self):
636 """Tests _CheckReplaceOperation() where op.type == REPLACE_XZ."""
637 payload_checker = checker.PayloadChecker(self.MockPayload())
638 block_size = payload_checker.block_size
639 data_length = block_size * 3
640
641 op = self.mox.CreateMock(
642 update_metadata_pb2.InstallOperation)
643 op.type = common.OpType.REPLACE_XZ
644
645 # Pass.
646 op.src_extents = []
647 self.assertIsNone(
648 payload_checker._CheckReplaceOperation(
649 op, data_length, (data_length + block_size - 1) / block_size + 5,
650 'foo'))
651
652 # Fail, src extents founds.
653 op.src_extents = ['bar']
654 self.assertRaises(
655 PayloadError, payload_checker._CheckReplaceOperation,
656 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
657
658 # Fail, missing data.
659 op.src_extents = []
660 self.assertRaises(
661 PayloadError, payload_checker._CheckReplaceOperation,
662 op, None, (data_length + block_size - 1) / block_size, 'foo')
663
664 # Fail, too few blocks to justify XZ.
665 op.src_extents = []
666 self.assertRaises(
667 PayloadError, payload_checker._CheckReplaceOperation,
668 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
669
Sen Jiang92161a72016-06-28 16:09:38 -0700670 def testCheckAnyDiff(self):
671 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800672 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800673 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800674
675 # Pass.
676 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800677 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800678
679 # Fail, missing data blob.
680 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800681 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800682 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800683
684 # Fail, too big of a diff blob (unjustified).
685 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800686 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800687 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800688
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800689 def testCheckSourceCopyOperation_Pass(self):
690 """Tests _CheckSourceCopyOperation(); pass case."""
691 payload_checker = checker.PayloadChecker(self.MockPayload())
692 self.assertIsNone(
693 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
694
695 def testCheckSourceCopyOperation_FailContainsData(self):
696 """Tests _CheckSourceCopyOperation(); message contains data."""
697 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800698 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800699 134, 0, 0, 'foo')
700
701 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
702 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
703 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800704 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800705 None, 0, 1, 'foo')
706
Gilad Arnold5502b562013-03-08 13:22:31 -0800707 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
708 allow_unhashed, fail_src_extents, fail_dst_extents,
709 fail_mismatched_data_offset_length,
710 fail_missing_dst_extents, fail_src_length,
711 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800712 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800713 """Parametric testing of _CheckOperation().
714
715 Args:
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700716 op_type_name: 'REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
Amin Hassani0de7f782017-12-07 12:13:03 -0800717 'SOURCE_COPY', 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700718 is_last: Whether we're testing the last operation in a sequence.
719 allow_signature: Whether we're testing a signature-capable operation.
720 allow_unhashed: Whether we're allowing to not hash the data.
721 fail_src_extents: Tamper with src extents.
722 fail_dst_extents: Tamper with dst extents.
723 fail_mismatched_data_offset_length: Make data_{offset,length}
724 inconsistent.
725 fail_missing_dst_extents: Do not include dst extents.
726 fail_src_length: Make src length inconsistent.
727 fail_dst_length: Make dst length inconsistent.
728 fail_data_hash: Tamper with the data blob hash.
729 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800730 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800731 """
732 op_type = _OpTypeByName(op_type_name)
733
734 # Create the test object.
735 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700736 payload_checker = checker.PayloadChecker(payload,
737 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800738 block_size = payload_checker.block_size
739
740 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700741 old_part_size = test_utils.MiB(4)
742 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800743 old_block_counters = array.array(
744 'B', [0] * ((old_part_size + block_size - 1) / block_size))
745 new_block_counters = array.array(
746 'B', [0] * ((new_part_size + block_size - 1) / block_size))
747 prev_data_offset = 1876
748 blob_hash_counts = collections.defaultdict(int)
749
750 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700751 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800752 op.type = op_type
753
754 total_src_blocks = 0
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700755 if op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800756 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800757 if fail_src_extents:
758 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700759 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800760 else:
761 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700762 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800763 total_src_blocks = 16
764
Tudor Brindus40506cd2018-06-18 20:18:17 -0700765 # TODO(tbrindus): add major version 2 tests.
Amin Hassani8ea19572018-12-05 12:06:21 -0800766 payload_checker.major_version = common.CHROMEOS_MAJOR_PAYLOAD_VERSION
Allie Wood7cf9f132015-02-26 14:28:19 -0800767 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700768 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800769 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700770 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassani0de7f782017-12-07 12:13:03 -0800771 if op_type == common.OpType.REPLACE_XZ:
772 payload_checker.minor_version = 2 if fail_bad_minor_version else 3
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700773 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800774 common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700775 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800776 elif op_type == common.OpType.PUFFDIFF:
777 payload_checker.minor_version = 4 if fail_bad_minor_version else 5
Allie Wood7cf9f132015-02-26 14:28:19 -0800778
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700779 if op_type != common.OpType.SOURCE_COPY:
Gilad Arnold5502b562013-03-08 13:22:31 -0800780 if not fail_mismatched_data_offset_length:
781 op.data_length = 16 * block_size - 8
782 if fail_prev_data_offset:
783 op.data_offset = prev_data_offset + 16
784 else:
785 op.data_offset = prev_data_offset
786
787 fake_data = 'fake-data'.ljust(op.data_length)
788 if not (allow_unhashed or (is_last and allow_signature and
789 op_type == common.OpType.REPLACE)):
790 if not fail_data_hash:
791 # Create a valid data blob hash.
792 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
793 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
794 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800795
Gilad Arnold5502b562013-03-08 13:22:31 -0800796 elif fail_data_hash:
797 # Create an invalid data blob hash.
798 op.data_sha256_hash = hashlib.sha256(
799 fake_data.replace(' ', '-')).digest()
800 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
801 fake_data)
802
803 total_dst_blocks = 0
804 if not fail_missing_dst_extents:
805 total_dst_blocks = 16
806 if fail_dst_extents:
807 self.AddToMessage(op.dst_extents,
808 self.NewExtentList((4, 16), (32, 0)))
809 else:
810 self.AddToMessage(op.dst_extents,
811 self.NewExtentList((4, 8), (64, 8)))
812
813 if total_src_blocks:
814 if fail_src_length:
815 op.src_length = total_src_blocks * block_size + 8
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700816 elif (op_type == common.OpType.SOURCE_BSDIFF and
Amin Hassaniefa62d92017-11-09 13:46:56 -0800817 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800818 op.src_length = total_src_blocks * block_size
819 elif fail_src_length:
820 # Add an orphaned src_length.
821 op.src_length = 16
822
823 if total_dst_blocks:
824 if fail_dst_length:
825 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassani0f59a9a2019-09-27 10:24:31 -0700826 elif (op_type == common.OpType.SOURCE_BSDIFF and
Amin Hassaniefa62d92017-11-09 13:46:56 -0800827 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800828 op.dst_length = total_dst_blocks * block_size
829
830 self.mox.ReplayAll()
831 should_fail = (fail_src_extents or fail_dst_extents or
832 fail_mismatched_data_offset_length or
833 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800834 fail_dst_length or fail_data_hash or fail_prev_data_offset or
835 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700836 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
837 old_part_size, new_part_size, prev_data_offset, allow_signature,
838 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800839 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800840 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800841 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700842 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
843 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800844
845 def testAllocBlockCounters(self):
846 """Tests _CheckMoveOperation()."""
847 payload_checker = checker.PayloadChecker(self.MockPayload())
848 block_size = payload_checker.block_size
849
850 # Check allocation for block-aligned partition size, ensure it's integers.
851 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700852 self.assertEqual(16, len(result))
853 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800854
855 # Check allocation of unaligned partition sizes.
856 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700857 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800858 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700859 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800860
Allie Woodfb04d302015-04-03 14:25:48 -0700861 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800862 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800863 # Generate a test payload. For this test, we only care about one
864 # (arbitrary) set of operations, so we'll only be generating kernel and
865 # test with them.
866 payload_gen = test_utils.PayloadGenerator()
867
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700868 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800869 payload_gen.SetBlockSize(block_size)
870
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700871 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800872
873 # Fake rootfs operations in a full update, tampered with as required.
874 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -0800875 rootfs_data_length = rootfs_part_size
876 if fail_nonexhaustive_full_update:
877 rootfs_data_length -= block_size
878
879 payload_gen.AddOperation(False, rootfs_op_type,
880 dst_extents=[(0, rootfs_data_length / block_size)],
881 data_offset=0,
882 data_length=rootfs_data_length)
883
884 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700885 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
886 checker_init_dargs={
887 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -0800888 payload_checker.payload_type = checker._TYPE_FULL
889 report = checker._PayloadReport()
890
Amin Hassaniae853742017-10-11 10:27:27 -0700891 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
892 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -0700893 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800894 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800895 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700896 self.assertEqual(rootfs_data_length,
897 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800898
899 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
900 fail_mismatched_pseudo_op, fail_sig_missing_fields,
901 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800902 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800903 # Generate a test payload. For this test, we only care about the signature
904 # block and how it relates to the payload hash. Therefore, we're generating
905 # a random (otherwise useless) payload for this purpose.
906 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700907 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800908 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700909 rootfs_part_size = test_utils.MiB(2)
910 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -0800911 payload_gen.SetPartInfo(False, True, rootfs_part_size,
912 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700913 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800914 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700915 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -0800916 payload_gen.AddOperationWithData(
917 False, common.OpType.REPLACE,
918 dst_extents=[(0, rootfs_part_size / block_size)],
919 data_blob=os.urandom(rootfs_part_size))
920
921 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
922 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
923 fail_sig_missing_fields or fail_unknown_sig_version
924 or fail_incorrect_sig)
925
926 sigs_data = None
927 if do_forge_sigs_data:
928 sigs_gen = test_utils.SignaturesGenerator()
929 if not fail_empty_sigs_blob:
930 if fail_sig_missing_fields:
931 sig_data = None
932 else:
933 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700934 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -0800935 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
936
937 sigs_data = sigs_gen.ToBinary()
938 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
939
940 if do_forge_pseudo_op:
941 assert sigs_data is not None, 'should have forged signatures blob by now'
942 sigs_len = len(sigs_data)
943 payload_gen.AddOperation(
944 False, common.OpType.REPLACE,
945 data_offset=payload_gen.curr_offset / 2,
946 data_length=sigs_len / 2,
947 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
948
949 # Generate payload (complete w/ signature) and create the test object.
950 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700951 payload_gen.WriteToFileWithData,
952 payload_gen_dargs={
953 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700954 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700955 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -0800956 payload_checker.payload_type = checker._TYPE_FULL
957 report = checker._PayloadReport()
958
959 # We have to check the manifest first in order to set signature attributes.
Tudor Brindus8d05a7e2018-06-14 11:18:18 -0700960 payload_checker._CheckManifest(report, {
961 common.ROOTFS: rootfs_part_size,
962 common.KERNEL: kernel_part_size
963 })
Gilad Arnold5502b562013-03-08 13:22:31 -0800964
965 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
966 fail_mismatched_pseudo_op or fail_sig_missing_fields or
967 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700968 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -0800969 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800970 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800971 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700972 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800973
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700974 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
975 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800976
977 Args:
978 minor_version: The payload minor version to test with.
979 payload_type: The type of the payload we're testing, delta or full.
980 """
981 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700982 payload = self.MockPayload()
983 payload.manifest.minor_version = minor_version
984 payload_checker = checker.PayloadChecker(payload)
985 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800986 report = checker._PayloadReport()
987
988 should_succeed = (
989 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
Sen Jiang912c4df2015-12-10 12:17:13 -0800990 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -0700991 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
Amin Hassani77d7cbc2018-02-07 16:21:33 -0800992 (minor_version == 4 and payload_type == checker._TYPE_DELTA) or
993 (minor_version == 5 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700994 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800995
996 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700997 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800998 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800999 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001000 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001001
Gilad Arnold06eea332015-07-13 18:06:33 -07001002 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1003 fail_wrong_payload_type, fail_invalid_block_size,
Amin Hassania86b1082018-03-08 15:48:59 -08001004 fail_mismatched_metadata_size, fail_mismatched_block_size,
1005 fail_excess_data, fail_rootfs_part_size_exceeded,
Gilad Arnold06eea332015-07-13 18:06:33 -07001006 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001007 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001008 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001009 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001010 # internal PayloadChecker methods that are tested elsewhere, here we only
1011 # tamper with what's actually being manipulated and/or tested in the Run()
1012 # method itself. Note that the checker doesn't verify partition hashes, so
1013 # they're safe to fake.
1014 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001015 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001016 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001017 kernel_filesystem_size = test_utils.KiB(16)
1018 rootfs_filesystem_size = test_utils.MiB(2)
1019 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001020 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001021 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001022 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001023 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001024
1025 rootfs_part_size = 0
1026 if rootfs_part_size_provided:
1027 rootfs_part_size = rootfs_filesystem_size + block_size
1028 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1029 if fail_rootfs_part_size_exceeded:
1030 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001031 payload_gen.AddOperationWithData(
1032 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001033 dst_extents=[(0, rootfs_op_size / block_size)],
1034 data_blob=os.urandom(rootfs_op_size))
1035
1036 kernel_part_size = 0
1037 if kernel_part_size_provided:
1038 kernel_part_size = kernel_filesystem_size + block_size
1039 kernel_op_size = kernel_part_size or kernel_filesystem_size
1040 if fail_kernel_part_size_exceeded:
1041 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001042 payload_gen.AddOperationWithData(
1043 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001044 dst_extents=[(0, kernel_op_size / block_size)],
1045 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001046
1047 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001048 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001049 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001050 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001051 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001052 else:
1053 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001054
Amin Hassania86b1082018-03-08 15:48:59 -08001055 # For the unittests 246 is the value that generated for the payload.
1056 metadata_size = 246
1057 if fail_mismatched_metadata_size:
1058 metadata_size += 1
1059
Gilad Arnoldcb638912013-06-24 04:57:11 -07001060 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001061 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001062 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001063 'do_add_pseudo_operation': True,
1064 'is_pseudo_in_kernel': True,
1065 'padding': os.urandom(1024) if fail_excess_data else None},
1066 'checker_init_dargs': {
1067 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1068 'block_size': use_block_size}}
1069 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001070 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001071 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001072 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001073 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001074 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001075
Tudor Brindus2d22c1a2018-06-15 13:07:13 -07001076 kwargs = {
1077 'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1078 'metadata_size': metadata_size,
1079 'part_sizes': {
1080 common.KERNEL: kernel_part_size,
1081 common.ROOTFS: rootfs_part_size}}
1082
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001083 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Amin Hassania86b1082018-03-08 15:48:59 -08001084 fail_mismatched_metadata_size or fail_excess_data or
Gilad Arnold06eea332015-07-13 18:06:33 -07001085 fail_rootfs_part_size_exceeded or
1086 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001087 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001088 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001089 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001090 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001091
Gilad Arnold5502b562013-03-08 13:22:31 -08001092# This implements a generic API, hence the occasional unused args.
1093# pylint: disable=W0613
1094def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1095 allow_unhashed, fail_src_extents,
1096 fail_dst_extents,
1097 fail_mismatched_data_offset_length,
1098 fail_missing_dst_extents, fail_src_length,
1099 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001100 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001101 """Returns True iff the combination of arguments represents a valid test."""
1102 op_type = _OpTypeByName(op_type_name)
1103
Amin Hassani0de7f782017-12-07 12:13:03 -08001104 # REPLACE/REPLACE_BZ/REPLACE_XZ operations don't read data from src
1105 # partition. They are compatible with all valid minor versions, so we don't
1106 # need to check that.
1107 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ,
1108 common.OpType.REPLACE_XZ) and (fail_src_extents or
1109 fail_src_length or
1110 fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001111 return False
1112
Amin Hassani0f59a9a2019-09-27 10:24:31 -07001113 # SOURCE_COPY operation does not carry data.
1114 if (op_type == common.OpType.SOURCE_COPY and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001115 fail_mismatched_data_offset_length or fail_data_hash or
1116 fail_prev_data_offset)):
1117 return False
1118
1119 return True
1120
1121
1122def TestMethodBody(run_method_name, run_dargs):
1123 """Returns a function that invokes a named method with named arguments."""
1124 return lambda self: getattr(self, run_method_name)(**run_dargs)
1125
1126
1127def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1128 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1129
1130 This function enumerates a space of test parameters (defined by arg_space),
1131 then binds a new, unique method name in PayloadCheckerTest to a test function
1132 that gets handed the said parameters. This is a preferable approach to doing
1133 the enumeration and invocation during the tests because this way each test is
1134 treated as a complete run by the unittest framework, and so benefits from the
1135 usual setUp/tearDown mechanics.
1136
1137 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001138 tested_method_name: Name of the tested PayloadChecker method.
1139 arg_space: A dictionary containing variables (keys) and lists of values
1140 (values) associated with them.
1141 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001142 """
1143 for value_tuple in itertools.product(*arg_space.itervalues()):
1144 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1145 if validate_func and not validate_func(**run_dargs):
1146 continue
1147 run_method_name = 'Do%sTest' % tested_method_name
1148 test_method_name = 'test%s' % tested_method_name
1149 for arg_key, arg_val in run_dargs.iteritems():
1150 if arg_val or type(arg_val) is int:
1151 test_method_name += '__%s=%s' % (arg_key, arg_val)
1152 setattr(PayloadCheckerTest, test_method_name,
1153 TestMethodBody(run_method_name, run_dargs))
1154
1155
1156def AddAllParametricTests():
1157 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1158 # Add all _CheckElem() test cases.
1159 AddParametricTests('AddElem',
1160 {'linebreak': (True, False),
1161 'indent': (0, 1, 2),
1162 'convert': (str, lambda s: s[::-1]),
1163 'is_present': (True, False),
1164 'is_mandatory': (True, False),
1165 'is_submsg': (True, False)})
1166
1167 # Add all _Add{Mandatory,Optional}Field tests.
1168 AddParametricTests('AddField',
1169 {'is_mandatory': (True, False),
1170 'linebreak': (True, False),
1171 'indent': (0, 1, 2),
1172 'convert': (str, lambda s: s[::-1]),
1173 'is_present': (True, False)})
1174
1175 # Add all _Add{Mandatory,Optional}SubMsg tests.
1176 AddParametricTests('AddSubMsg',
1177 {'is_mandatory': (True, False),
1178 'is_present': (True, False)})
1179
1180 # Add all _CheckManifest() test cases.
1181 AddParametricTests('CheckManifest',
1182 {'fail_mismatched_block_size': (True, False),
1183 'fail_bad_sigs': (True, False),
1184 'fail_mismatched_oki_ori': (True, False),
1185 'fail_bad_oki': (True, False),
1186 'fail_bad_ori': (True, False),
1187 'fail_bad_nki': (True, False),
1188 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001189 'fail_old_kernel_fs_size': (True, False),
1190 'fail_old_rootfs_fs_size': (True, False),
1191 'fail_new_kernel_fs_size': (True, False),
1192 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001193
1194 # Add all _CheckOperation() test cases.
1195 AddParametricTests('CheckOperation',
Amin Hassani0de7f782017-12-07 12:13:03 -08001196 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'REPLACE_XZ',
Amin Hassani0f59a9a2019-09-27 10:24:31 -07001197 'SOURCE_COPY', 'SOURCE_BSDIFF',
1198 'PUFFDIFF', 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001199 'is_last': (True, False),
1200 'allow_signature': (True, False),
1201 'allow_unhashed': (True, False),
1202 'fail_src_extents': (True, False),
1203 'fail_dst_extents': (True, False),
1204 'fail_mismatched_data_offset_length': (True, False),
1205 'fail_missing_dst_extents': (True, False),
1206 'fail_src_length': (True, False),
1207 'fail_dst_length': (True, False),
1208 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001209 'fail_prev_data_offset': (True, False),
1210 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001211 validate_func=ValidateCheckOperationTest)
1212
1213 # Add all _CheckOperations() test cases.
1214 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001215 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001216
1217 # Add all _CheckOperations() test cases.
1218 AddParametricTests('CheckSignatures',
1219 {'fail_empty_sigs_blob': (True, False),
1220 'fail_missing_pseudo_op': (True, False),
1221 'fail_mismatched_pseudo_op': (True, False),
1222 'fail_sig_missing_fields': (True, False),
1223 'fail_unknown_sig_version': (True, False),
1224 'fail_incorrect_sig': (True, False)})
1225
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001226 # Add all _CheckManifestMinorVersion() test cases.
1227 AddParametricTests('CheckManifestMinorVersion',
Amin Hassani0f59a9a2019-09-27 10:24:31 -07001228 {'minor_version': (None, 0, 2, 3, 4, 5, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001229 'payload_type': (checker._TYPE_FULL,
1230 checker._TYPE_DELTA)})
1231
Gilad Arnold5502b562013-03-08 13:22:31 -08001232 # Add all Run() test cases.
1233 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001234 {'rootfs_part_size_provided': (True, False),
1235 'kernel_part_size_provided': (True, False),
1236 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001237 'fail_invalid_block_size': (True, False),
Amin Hassania86b1082018-03-08 15:48:59 -08001238 'fail_mismatched_metadata_size': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001239 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001240 'fail_excess_data': (True, False),
1241 'fail_rootfs_part_size_exceeded': (True, False),
1242 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001243
1244
1245if __name__ == '__main__':
1246 AddAllParametricTests()
1247 unittest.main()