blob: 974519d5e64cb4e630c40c0aaa7afb016423a666 [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
Amin Hassanib05a65a2017-12-18 15:15:32 -080023from update_payload import checker
24from update_payload import common
25from update_payload import test_utils
26from update_payload import update_metadata_pb2
27from update_payload.error import PayloadError
28from update_payload.payload import Payload # Avoid name conflicts later.
Gilad Arnold5502b562013-03-08 13:22:31 -080029
30
Gilad Arnold5502b562013-03-08 13:22:31 -080031def _OpTypeByName(op_name):
Amin Hassanib05a65a2017-12-18 15:15:32 -080032 """Returns the type of an operation from itsname."""
Gilad Arnold5502b562013-03-08 13:22:31 -080033 op_name_to_type = {
34 'REPLACE': common.OpType.REPLACE,
35 'REPLACE_BZ': common.OpType.REPLACE_BZ,
36 'MOVE': common.OpType.MOVE,
37 'BSDIFF': common.OpType.BSDIFF,
Allie Woodf5c4f3e2015-02-20 16:57:46 -080038 'SOURCE_COPY': common.OpType.SOURCE_COPY,
39 'SOURCE_BSDIFF': common.OpType.SOURCE_BSDIFF,
Alex Deymo28466772015-09-11 17:16:44 -070040 'ZERO': common.OpType.ZERO,
41 'DISCARD': common.OpType.DISCARD,
42 'REPLACE_XZ': common.OpType.REPLACE_XZ,
Amin Hassani5ef5d452017-08-04 13:10:59 -070043 'PUFFDIFF': common.OpType.PUFFDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -080044 'BROTLI_BSDIFF': common.OpType.BROTLI_BSDIFF,
Gilad Arnold5502b562013-03-08 13:22:31 -080045 }
46 return op_name_to_type[op_name]
47
48
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070049def _GetPayloadChecker(payload_gen_write_to_file_func, payload_gen_dargs=None,
50 checker_init_dargs=None):
Gilad Arnold5502b562013-03-08 13:22:31 -080051 """Returns a payload checker from a given payload generator."""
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070052 if payload_gen_dargs is None:
53 payload_gen_dargs = {}
54 if checker_init_dargs is None:
55 checker_init_dargs = {}
56
Gilad Arnold5502b562013-03-08 13:22:31 -080057 payload_file = cStringIO.StringIO()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070058 payload_gen_write_to_file_func(payload_file, **payload_gen_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080059 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080060 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080061 payload.Init()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070062 return checker.PayloadChecker(payload, **checker_init_dargs)
Gilad Arnold5502b562013-03-08 13:22:31 -080063
64
65def _GetPayloadCheckerWithData(payload_gen):
66 """Returns a payload checker from a given payload generator."""
67 payload_file = cStringIO.StringIO()
68 payload_gen.WriteToFile(payload_file)
69 payload_file.seek(0)
Amin Hassanib05a65a2017-12-18 15:15:32 -080070 payload = Payload(payload_file)
Gilad Arnold5502b562013-03-08 13:22:31 -080071 payload.Init()
72 return checker.PayloadChecker(payload)
73
74
Gilad Arnoldcb638912013-06-24 04:57:11 -070075# This class doesn't need an __init__().
Gilad Arnold5502b562013-03-08 13:22:31 -080076# pylint: disable=W0232
Gilad Arnoldcb638912013-06-24 04:57:11 -070077# Unit testing is all about running protected methods.
Gilad Arnold5502b562013-03-08 13:22:31 -080078# pylint: disable=W0212
Gilad Arnoldcb638912013-06-24 04:57:11 -070079# Don't bark about missing members of classes you cannot import.
Gilad Arnold5502b562013-03-08 13:22:31 -080080# pylint: disable=E1101
81class PayloadCheckerTest(mox.MoxTestBase):
82 """Tests the PayloadChecker class.
83
84 In addition to ordinary testFoo() methods, which are automatically invoked by
85 the unittest framework, in this class we make use of DoBarTest() calls that
86 implement parametric tests of certain features. In order to invoke each test,
87 which embodies a unique combination of parameter values, as a complete unit
88 test, we perform explicit enumeration of the parameter space and create
89 individual invocation contexts for each, which are then bound as
90 testBar__param1=val1__param2=val2(). The enumeration of parameter spaces for
91 all such tests is done in AddAllParametricTests().
Gilad Arnold5502b562013-03-08 13:22:31 -080092 """
93
94 def MockPayload(self):
Allie Woodf5c4f3e2015-02-20 16:57:46 -080095 """Create a mock payload object, complete with a mock manifest."""
Amin Hassanib05a65a2017-12-18 15:15:32 -080096 payload = self.mox.CreateMock(Payload)
Gilad Arnold5502b562013-03-08 13:22:31 -080097 payload.is_init = True
98 payload.manifest = self.mox.CreateMock(
99 update_metadata_pb2.DeltaArchiveManifest)
100 return payload
101
102 @staticmethod
103 def NewExtent(start_block, num_blocks):
104 """Returns an Extent message.
105
106 Each of the provided fields is set iff it is >= 0; otherwise, it's left at
107 its default state.
108
109 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700110 start_block: The starting block of the extent.
111 num_blocks: The number of blocks in the extent.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800112
Gilad Arnold5502b562013-03-08 13:22:31 -0800113 Returns:
114 An Extent message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800115 """
116 ex = update_metadata_pb2.Extent()
117 if start_block >= 0:
118 ex.start_block = start_block
119 if num_blocks >= 0:
120 ex.num_blocks = num_blocks
121 return ex
122
123 @staticmethod
124 def NewExtentList(*args):
125 """Returns an list of extents.
126
127 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700128 *args: (start_block, num_blocks) pairs defining the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800129
Gilad Arnold5502b562013-03-08 13:22:31 -0800130 Returns:
131 A list of Extent objects.
Gilad Arnold5502b562013-03-08 13:22:31 -0800132 """
133 ex_list = []
134 for start_block, num_blocks in args:
135 ex_list.append(PayloadCheckerTest.NewExtent(start_block, num_blocks))
136 return ex_list
137
138 @staticmethod
139 def AddToMessage(repeated_field, field_vals):
140 for field_val in field_vals:
141 new_field = repeated_field.add()
142 new_field.CopyFrom(field_val)
143
Gilad Arnold5502b562013-03-08 13:22:31 -0800144 def SetupAddElemTest(self, is_present, is_submsg, convert=str,
145 linebreak=False, indent=0):
146 """Setup for testing of _CheckElem() and its derivatives.
147
148 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700149 is_present: Whether or not the element is found in the message.
150 is_submsg: Whether the element is a sub-message itself.
151 convert: A representation conversion function.
152 linebreak: Whether or not a linebreak is to be used in the report.
153 indent: Indentation used for the report.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800154
Gilad Arnold5502b562013-03-08 13:22:31 -0800155 Returns:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700156 msg: A mock message object.
157 report: A mock report object.
158 subreport: A mock sub-report object.
159 name: An element name to check.
160 val: Expected element value.
Gilad Arnold5502b562013-03-08 13:22:31 -0800161 """
162 name = 'foo'
163 val = 'fake submsg' if is_submsg else 'fake field'
164 subreport = 'fake subreport'
165
166 # Create a mock message.
Alex Deymo28466772015-09-11 17:16:44 -0700167 msg = self.mox.CreateMock(update_metadata_pb2._message.Message)
Gilad Arnold5502b562013-03-08 13:22:31 -0800168 msg.HasField(name).AndReturn(is_present)
169 setattr(msg, name, val)
170
171 # Create a mock report.
172 report = self.mox.CreateMock(checker._PayloadReport)
173 if is_present:
174 if is_submsg:
175 report.AddSubReport(name).AndReturn(subreport)
176 else:
177 report.AddField(name, convert(val), linebreak=linebreak, indent=indent)
178
179 self.mox.ReplayAll()
180 return (msg, report, subreport, name, val)
181
182 def DoAddElemTest(self, is_present, is_mandatory, is_submsg, convert,
183 linebreak, indent):
184 """Parametric testing of _CheckElem().
185
186 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700187 is_present: Whether or not the element is found in the message.
188 is_mandatory: Whether or not it's a mandatory element.
189 is_submsg: Whether the element is a sub-message itself.
190 convert: A representation conversion function.
191 linebreak: Whether or not a linebreak is to be used in the report.
192 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800193 """
194 msg, report, subreport, name, val = self.SetupAddElemTest(
195 is_present, is_submsg, convert, linebreak, indent)
196
Gilad Arnoldcb638912013-06-24 04:57:11 -0700197 args = (msg, name, report, is_mandatory, is_submsg)
198 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800199 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800200 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700201 checker.PayloadChecker._CheckElem, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800202 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700203 ret_val, ret_subreport = checker.PayloadChecker._CheckElem(*args,
204 **kwargs)
205 self.assertEquals(val if is_present else None, ret_val)
206 self.assertEquals(subreport if is_present and is_submsg else None,
207 ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800208
209 def DoAddFieldTest(self, is_mandatory, is_present, convert, linebreak,
210 indent):
211 """Parametric testing of _Check{Mandatory,Optional}Field().
212
213 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700214 is_mandatory: Whether we're testing a mandatory call.
215 is_present: Whether or not the element is found in the message.
216 convert: A representation conversion function.
217 linebreak: Whether or not a linebreak is to be used in the report.
218 indent: Indentation used for the report.
Gilad Arnold5502b562013-03-08 13:22:31 -0800219 """
220 msg, report, _, name, val = self.SetupAddElemTest(
221 is_present, False, convert, linebreak, indent)
222
223 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700224 args = [msg, name, report]
225 kwargs = {'convert': convert, 'linebreak': linebreak, 'indent': indent}
Gilad Arnold5502b562013-03-08 13:22:31 -0800226 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700227 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800228 tested_func = checker.PayloadChecker._CheckMandatoryField
229 else:
230 tested_func = checker.PayloadChecker._CheckOptionalField
231
232 # Test the method call.
233 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800234 self.assertRaises(PayloadError, tested_func, *args, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -0800235 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700236 ret_val = tested_func(*args, **kwargs)
237 self.assertEquals(val if is_present else None, ret_val)
Gilad Arnold5502b562013-03-08 13:22:31 -0800238
239 def DoAddSubMsgTest(self, is_mandatory, is_present):
240 """Parametrized testing of _Check{Mandatory,Optional}SubMsg().
241
242 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700243 is_mandatory: Whether we're testing a mandatory call.
244 is_present: Whether or not the element is found in the message.
Gilad Arnold5502b562013-03-08 13:22:31 -0800245 """
246 msg, report, subreport, name, val = self.SetupAddElemTest(is_present, True)
247
248 # Prepare for invocation of the tested method.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700249 args = [msg, name, report]
Gilad Arnold5502b562013-03-08 13:22:31 -0800250 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700251 args.append('bar')
Gilad Arnold5502b562013-03-08 13:22:31 -0800252 tested_func = checker.PayloadChecker._CheckMandatorySubMsg
253 else:
254 tested_func = checker.PayloadChecker._CheckOptionalSubMsg
255
256 # Test the method call.
257 if is_mandatory and not is_present:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800258 self.assertRaises(PayloadError, tested_func, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800259 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700260 ret_val, ret_subreport = tested_func(*args)
261 self.assertEquals(val if is_present else None, ret_val)
262 self.assertEquals(subreport if is_present else None, ret_subreport)
Gilad Arnold5502b562013-03-08 13:22:31 -0800263
264 def testCheckPresentIff(self):
265 """Tests _CheckPresentIff()."""
266 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
267 None, None, 'foo', 'bar', 'baz'))
268 self.assertIsNone(checker.PayloadChecker._CheckPresentIff(
269 'a', 'b', 'foo', 'bar', 'baz'))
Amin Hassanib05a65a2017-12-18 15:15:32 -0800270 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800271 'a', None, 'foo', 'bar', 'baz')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800272 self.assertRaises(PayloadError, checker.PayloadChecker._CheckPresentIff,
Gilad Arnold5502b562013-03-08 13:22:31 -0800273 None, 'b', 'foo', 'bar', 'baz')
274
275 def DoCheckSha256SignatureTest(self, expect_pass, expect_subprocess_call,
276 sig_data, sig_asn1_header,
277 returned_signed_hash, expected_signed_hash):
278 """Parametric testing of _CheckSha256SignatureTest().
279
280 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700281 expect_pass: Whether or not it should pass.
282 expect_subprocess_call: Whether to expect the openssl call to happen.
283 sig_data: The signature raw data.
284 sig_asn1_header: The ASN1 header.
285 returned_signed_hash: The signed hash data retuned by openssl.
286 expected_signed_hash: The signed hash data to compare against.
Gilad Arnold5502b562013-03-08 13:22:31 -0800287 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700288 try:
289 # Stub out the subprocess invocation.
290 self.mox.StubOutWithMock(checker.PayloadChecker, '_Run')
291 if expect_subprocess_call:
292 checker.PayloadChecker._Run(
293 mox.IsA(list), send_data=sig_data).AndReturn(
294 (sig_asn1_header + returned_signed_hash, None))
Gilad Arnold5502b562013-03-08 13:22:31 -0800295
Gilad Arnoldcb638912013-06-24 04:57:11 -0700296 self.mox.ReplayAll()
297 if expect_pass:
298 self.assertIsNone(checker.PayloadChecker._CheckSha256Signature(
299 sig_data, 'foo', expected_signed_hash, 'bar'))
300 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800301 self.assertRaises(PayloadError,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700302 checker.PayloadChecker._CheckSha256Signature,
303 sig_data, 'foo', expected_signed_hash, 'bar')
304 finally:
305 self.mox.UnsetStubs()
Gilad Arnold5502b562013-03-08 13:22:31 -0800306
307 def testCheckSha256Signature_Pass(self):
308 """Tests _CheckSha256Signature(); pass case."""
309 sig_data = 'fake-signature'.ljust(256)
310 signed_hash = hashlib.sha256('fake-data').digest()
311 self.DoCheckSha256SignatureTest(True, True, sig_data,
312 common.SIG_ASN1_HEADER, signed_hash,
313 signed_hash)
314
315 def testCheckSha256Signature_FailBadSignature(self):
316 """Tests _CheckSha256Signature(); fails due to malformed signature."""
Gilad Arnoldcb638912013-06-24 04:57:11 -0700317 sig_data = 'fake-signature' # Malformed (not 256 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800318 signed_hash = hashlib.sha256('fake-data').digest()
319 self.DoCheckSha256SignatureTest(False, False, sig_data,
320 common.SIG_ASN1_HEADER, signed_hash,
321 signed_hash)
322
323 def testCheckSha256Signature_FailBadOutputLength(self):
324 """Tests _CheckSha256Signature(); fails due to unexpected output length."""
325 sig_data = 'fake-signature'.ljust(256)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700326 signed_hash = 'fake-hash' # Malformed (not 32 bytes in length).
Gilad Arnold5502b562013-03-08 13:22:31 -0800327 self.DoCheckSha256SignatureTest(False, True, sig_data,
328 common.SIG_ASN1_HEADER, signed_hash,
329 signed_hash)
330
331 def testCheckSha256Signature_FailBadAsnHeader(self):
332 """Tests _CheckSha256Signature(); fails due to bad ASN1 header."""
333 sig_data = 'fake-signature'.ljust(256)
334 signed_hash = hashlib.sha256('fake-data').digest()
335 bad_asn1_header = 'bad-asn-header'.ljust(len(common.SIG_ASN1_HEADER))
336 self.DoCheckSha256SignatureTest(False, True, sig_data, bad_asn1_header,
337 signed_hash, signed_hash)
338
339 def testCheckSha256Signature_FailBadHash(self):
340 """Tests _CheckSha256Signature(); fails due to bad hash returned."""
341 sig_data = 'fake-signature'.ljust(256)
342 expected_signed_hash = hashlib.sha256('fake-data').digest()
343 returned_signed_hash = hashlib.sha256('bad-fake-data').digest()
344 self.DoCheckSha256SignatureTest(False, True, sig_data,
345 common.SIG_ASN1_HEADER,
346 expected_signed_hash, returned_signed_hash)
347
348 def testCheckBlocksFitLength_Pass(self):
349 """Tests _CheckBlocksFitLength(); pass case."""
350 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
351 64, 4, 16, 'foo'))
352 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
353 60, 4, 16, 'foo'))
354 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
355 49, 4, 16, 'foo'))
356 self.assertIsNone(checker.PayloadChecker._CheckBlocksFitLength(
357 48, 3, 16, 'foo'))
358
359 def testCheckBlocksFitLength_TooManyBlocks(self):
360 """Tests _CheckBlocksFitLength(); fails due to excess blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800361 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800362 checker.PayloadChecker._CheckBlocksFitLength,
363 64, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800364 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800365 checker.PayloadChecker._CheckBlocksFitLength,
366 60, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800367 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800368 checker.PayloadChecker._CheckBlocksFitLength,
369 49, 5, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800370 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800371 checker.PayloadChecker._CheckBlocksFitLength,
372 48, 4, 16, 'foo')
373
374 def testCheckBlocksFitLength_TooFewBlocks(self):
375 """Tests _CheckBlocksFitLength(); fails due to insufficient blocks."""
Amin Hassanib05a65a2017-12-18 15:15:32 -0800376 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800377 checker.PayloadChecker._CheckBlocksFitLength,
378 64, 3, 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 60, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800382 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800383 checker.PayloadChecker._CheckBlocksFitLength,
384 49, 3, 16, 'foo')
Amin Hassanib05a65a2017-12-18 15:15:32 -0800385 self.assertRaises(PayloadError,
Gilad Arnold5502b562013-03-08 13:22:31 -0800386 checker.PayloadChecker._CheckBlocksFitLength,
387 48, 2, 16, 'foo')
388
389 def DoCheckManifestTest(self, fail_mismatched_block_size, fail_bad_sigs,
390 fail_mismatched_oki_ori, fail_bad_oki, fail_bad_ori,
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800391 fail_bad_nki, fail_bad_nri, fail_old_kernel_fs_size,
392 fail_old_rootfs_fs_size, fail_new_kernel_fs_size,
393 fail_new_rootfs_fs_size):
Gilad Arnold5502b562013-03-08 13:22:31 -0800394 """Parametric testing of _CheckManifest().
395
396 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700397 fail_mismatched_block_size: Simulate a missing block_size field.
398 fail_bad_sigs: Make signatures descriptor inconsistent.
399 fail_mismatched_oki_ori: Make old rootfs/kernel info partially present.
400 fail_bad_oki: Tamper with old kernel info.
401 fail_bad_ori: Tamper with old rootfs info.
402 fail_bad_nki: Tamper with new kernel info.
403 fail_bad_nri: Tamper with new rootfs info.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700404 fail_old_kernel_fs_size: Make old kernel fs size too big.
405 fail_old_rootfs_fs_size: Make old rootfs fs size too big.
406 fail_new_kernel_fs_size: Make new kernel fs size too big.
407 fail_new_rootfs_fs_size: Make new rootfs fs size too big.
Gilad Arnold5502b562013-03-08 13:22:31 -0800408 """
409 # Generate a test payload. For this test, we only care about the manifest
410 # and don't need any data blobs, hence we can use a plain paylaod generator
411 # (which also gives us more control on things that can be screwed up).
412 payload_gen = test_utils.PayloadGenerator()
413
414 # Tamper with block size, if required.
415 if fail_mismatched_block_size:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700416 payload_gen.SetBlockSize(test_utils.KiB(1))
Gilad Arnold5502b562013-03-08 13:22:31 -0800417 else:
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700418 payload_gen.SetBlockSize(test_utils.KiB(4))
Gilad Arnold5502b562013-03-08 13:22:31 -0800419
420 # Add some operations.
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800421 payload_gen.AddOperation(False, common.OpType.MOVE,
422 src_extents=[(0, 16), (16, 497)],
423 dst_extents=[(16, 496), (0, 16)])
424 payload_gen.AddOperation(True, common.OpType.MOVE,
425 src_extents=[(0, 8), (8, 8)],
426 dst_extents=[(8, 8), (0, 8)])
Gilad Arnold5502b562013-03-08 13:22:31 -0800427
428 # Set an invalid signatures block (offset but no size), if required.
429 if fail_bad_sigs:
430 payload_gen.SetSignatures(32, None)
431
Gilad Arnold382df5c2013-05-03 12:49:28 -0700432 # Set partition / filesystem sizes.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700433 rootfs_part_size = test_utils.MiB(8)
434 kernel_part_size = test_utils.KiB(512)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700435 old_rootfs_fs_size = new_rootfs_fs_size = rootfs_part_size
436 old_kernel_fs_size = new_kernel_fs_size = kernel_part_size
437 if fail_old_kernel_fs_size:
438 old_kernel_fs_size += 100
439 if fail_old_rootfs_fs_size:
440 old_rootfs_fs_size += 100
441 if fail_new_kernel_fs_size:
442 new_kernel_fs_size += 100
443 if fail_new_rootfs_fs_size:
444 new_rootfs_fs_size += 100
445
Gilad Arnold5502b562013-03-08 13:22:31 -0800446 # Add old kernel/rootfs partition info, as required.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700447 if fail_mismatched_oki_ori or fail_old_kernel_fs_size or fail_bad_oki:
Gilad Arnold5502b562013-03-08 13:22:31 -0800448 oki_hash = (None if fail_bad_oki
449 else hashlib.sha256('fake-oki-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700450 payload_gen.SetPartInfo(True, False, old_kernel_fs_size, oki_hash)
451 if not fail_mismatched_oki_ori and (fail_old_rootfs_fs_size or
452 fail_bad_ori):
453 ori_hash = (None if fail_bad_ori
454 else hashlib.sha256('fake-ori-content').digest())
455 payload_gen.SetPartInfo(False, False, old_rootfs_fs_size, ori_hash)
Gilad Arnold5502b562013-03-08 13:22:31 -0800456
457 # Add new kernel/rootfs partition info.
458 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700459 True, True, new_kernel_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800460 None if fail_bad_nki else hashlib.sha256('fake-nki-content').digest())
461 payload_gen.SetPartInfo(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700462 False, True, new_rootfs_fs_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800463 None if fail_bad_nri else hashlib.sha256('fake-nri-content').digest())
464
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700465 # Set the minor version.
466 payload_gen.SetMinorVersion(0)
467
Gilad Arnold5502b562013-03-08 13:22:31 -0800468 # Create the test object.
469 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile)
470 report = checker._PayloadReport()
471
472 should_fail = (fail_mismatched_block_size or fail_bad_sigs or
473 fail_mismatched_oki_ori or fail_bad_oki or fail_bad_ori or
Gilad Arnold5bc7fbe2015-02-05 13:01:09 -0800474 fail_bad_nki or fail_bad_nri or fail_old_kernel_fs_size or
475 fail_old_rootfs_fs_size or fail_new_kernel_fs_size or
476 fail_new_rootfs_fs_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800477 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800478 self.assertRaises(PayloadError, payload_checker._CheckManifest, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -0700479 rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -0800480 else:
Gilad Arnold382df5c2013-05-03 12:49:28 -0700481 self.assertIsNone(payload_checker._CheckManifest(report,
482 rootfs_part_size,
483 kernel_part_size))
Gilad Arnold5502b562013-03-08 13:22:31 -0800484
485 def testCheckLength(self):
486 """Tests _CheckLength()."""
487 payload_checker = checker.PayloadChecker(self.MockPayload())
488 block_size = payload_checker.block_size
489
490 # Passes.
491 self.assertIsNone(payload_checker._CheckLength(
492 int(3.5 * block_size), 4, 'foo', 'bar'))
493 # Fails, too few blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800494 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800495 int(3.5 * block_size), 3, 'foo', 'bar')
496 # Fails, too many blocks.
Amin Hassanib05a65a2017-12-18 15:15:32 -0800497 self.assertRaises(PayloadError, payload_checker._CheckLength,
Gilad Arnold5502b562013-03-08 13:22:31 -0800498 int(3.5 * block_size), 5, 'foo', 'bar')
499
500 def testCheckExtents(self):
501 """Tests _CheckExtents()."""
502 payload_checker = checker.PayloadChecker(self.MockPayload())
503 block_size = payload_checker.block_size
504
505 # Passes w/ all real extents.
506 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
507 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700508 23,
Gilad Arnold5502b562013-03-08 13:22:31 -0800509 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
Gilad Arnoldcb638912013-06-24 04:57:11 -0700510 collections.defaultdict(int), 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800511
512 # Passes w/ pseudo-extents (aka sparse holes).
513 extents = self.NewExtentList((0, 4), (common.PSEUDO_EXTENT_MARKER, 5),
514 (8, 3))
515 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700516 12,
Gilad Arnold5502b562013-03-08 13:22:31 -0800517 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
518 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700519 allow_pseudo=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800520
521 # Passes w/ pseudo-extent due to a signature.
522 extents = self.NewExtentList((common.PSEUDO_EXTENT_MARKER, 2))
523 self.assertEquals(
Gilad Arnoldcb638912013-06-24 04:57:11 -0700524 2,
Gilad Arnold5502b562013-03-08 13:22:31 -0800525 payload_checker._CheckExtents(extents, (1024 + 16) * block_size,
526 collections.defaultdict(int), 'foo',
Gilad Arnoldcb638912013-06-24 04:57:11 -0700527 allow_signature=True))
Gilad Arnold5502b562013-03-08 13:22:31 -0800528
529 # Fails, extent missing a start block.
530 extents = self.NewExtentList((-1, 4), (8, 3), (1024, 16))
531 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800532 PayloadError, payload_checker._CheckExtents, extents,
533 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800534
535 # Fails, extent missing block count.
536 extents = self.NewExtentList((0, -1), (8, 3), (1024, 16))
537 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800538 PayloadError, payload_checker._CheckExtents, extents,
539 (1024 + 16) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800540
541 # Fails, extent has zero blocks.
542 extents = self.NewExtentList((0, 4), (8, 3), (1024, 0))
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 exceeds partition boundaries.
548 extents = self.NewExtentList((0, 4), (8, 3), (1024, 16))
549 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800550 PayloadError, payload_checker._CheckExtents, extents,
551 (1024 + 15) * block_size, collections.defaultdict(int), 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800552
553 def testCheckReplaceOperation(self):
554 """Tests _CheckReplaceOperation() where op.type == REPLACE."""
555 payload_checker = checker.PayloadChecker(self.MockPayload())
556 block_size = payload_checker.block_size
557 data_length = 10000
558
559 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700560 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800561 op.type = common.OpType.REPLACE
562
563 # Pass.
564 op.src_extents = []
565 self.assertIsNone(
566 payload_checker._CheckReplaceOperation(
567 op, data_length, (data_length + block_size - 1) / block_size,
568 'foo'))
569
570 # Fail, src extents founds.
571 op.src_extents = ['bar']
572 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800573 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800574 op, data_length, (data_length + block_size - 1) / block_size, 'foo')
575
576 # Fail, missing data.
577 op.src_extents = []
578 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800579 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800580 op, None, (data_length + block_size - 1) / block_size, 'foo')
581
582 # Fail, length / block number mismatch.
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 + 1, 'foo')
587
588 def testCheckReplaceBzOperation(self):
589 """Tests _CheckReplaceOperation() where op.type == REPLACE_BZ."""
590 payload_checker = checker.PayloadChecker(self.MockPayload())
591 block_size = payload_checker.block_size
592 data_length = block_size * 3
593
594 op = self.mox.CreateMock(
Alex Deymo28466772015-09-11 17:16:44 -0700595 update_metadata_pb2.InstallOperation)
Gilad Arnold5502b562013-03-08 13:22:31 -0800596 op.type = common.OpType.REPLACE_BZ
597
598 # Pass.
599 op.src_extents = []
600 self.assertIsNone(
601 payload_checker._CheckReplaceOperation(
602 op, data_length, (data_length + block_size - 1) / block_size + 5,
603 'foo'))
604
605 # Fail, src extents founds.
606 op.src_extents = ['bar']
607 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800608 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800609 op, data_length, (data_length + block_size - 1) / block_size + 5, 'foo')
610
611 # Fail, missing data.
612 op.src_extents = []
613 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800614 PayloadError, payload_checker._CheckReplaceOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800615 op, None, (data_length + block_size - 1) / block_size, 'foo')
616
617 # Fail, too few blocks to justify BZ.
618 op.src_extents = []
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, 'foo')
622
623 def testCheckMoveOperation_Pass(self):
624 """Tests _CheckMoveOperation(); pass case."""
625 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700626 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800627 op.type = common.OpType.MOVE
628
629 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700630 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800631 self.AddToMessage(op.dst_extents,
632 self.NewExtentList((16, 128), (512, 6)))
633 self.assertIsNone(
634 payload_checker._CheckMoveOperation(op, None, 134, 134, 'foo'))
635
636 def testCheckMoveOperation_FailContainsData(self):
637 """Tests _CheckMoveOperation(); fails, message contains data."""
638 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700639 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800640 op.type = common.OpType.MOVE
641
642 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700643 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800644 self.AddToMessage(op.dst_extents,
645 self.NewExtentList((16, 128), (512, 6)))
646 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800647 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800648 op, 1024, 134, 134, 'foo')
649
650 def testCheckMoveOperation_FailInsufficientSrcBlocks(self):
651 """Tests _CheckMoveOperation(); fails, not enough actual src blocks."""
652 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700653 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800654 op.type = common.OpType.MOVE
655
656 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700657 self.NewExtentList((1, 4), (12, 2), (1024, 127)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800658 self.AddToMessage(op.dst_extents,
659 self.NewExtentList((16, 128), (512, 6)))
660 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800661 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800662 op, None, 134, 134, 'foo')
663
664 def testCheckMoveOperation_FailInsufficientDstBlocks(self):
665 """Tests _CheckMoveOperation(); fails, not enough actual dst blocks."""
666 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700667 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800668 op.type = common.OpType.MOVE
669
670 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700671 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800672 self.AddToMessage(op.dst_extents,
673 self.NewExtentList((16, 128), (512, 5)))
674 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800675 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800676 op, None, 134, 134, 'foo')
677
678 def testCheckMoveOperation_FailExcessSrcBlocks(self):
679 """Tests _CheckMoveOperation(); fails, too many actual src blocks."""
680 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700681 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800682 op.type = common.OpType.MOVE
683
684 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700685 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800686 self.AddToMessage(op.dst_extents,
687 self.NewExtentList((16, 128), (512, 5)))
688 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800689 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800690 op, None, 134, 134, 'foo')
691 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700692 self.NewExtentList((1, 4), (12, 2), (1024, 129)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800693 self.AddToMessage(op.dst_extents,
694 self.NewExtentList((16, 128), (512, 6)))
695 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800696 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800697 op, None, 134, 134, 'foo')
698
699 def testCheckMoveOperation_FailExcessDstBlocks(self):
700 """Tests _CheckMoveOperation(); fails, too many actual dst blocks."""
701 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700702 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800703 op.type = common.OpType.MOVE
704
705 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700706 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800707 self.AddToMessage(op.dst_extents,
708 self.NewExtentList((16, 128), (512, 7)))
709 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800710 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800711 op, None, 134, 134, 'foo')
712
713 def testCheckMoveOperation_FailStagnantBlocks(self):
714 """Tests _CheckMoveOperation(); fails, there are blocks that do not move."""
715 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700716 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800717 op.type = common.OpType.MOVE
718
719 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700720 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
721 self.AddToMessage(op.dst_extents,
722 self.NewExtentList((8, 128), (512, 6)))
723 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800724 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700725 op, None, 134, 134, 'foo')
726
727 def testCheckMoveOperation_FailZeroStartBlock(self):
728 """Tests _CheckMoveOperation(); fails, has extent with start block 0."""
729 payload_checker = checker.PayloadChecker(self.MockPayload())
Alex Deymo28466772015-09-11 17:16:44 -0700730 op = update_metadata_pb2.InstallOperation()
Allie Woodb065e132015-04-24 10:20:27 -0700731 op.type = common.OpType.MOVE
732
733 self.AddToMessage(op.src_extents,
Gilad Arnold5502b562013-03-08 13:22:31 -0800734 self.NewExtentList((0, 4), (12, 2), (1024, 128)))
735 self.AddToMessage(op.dst_extents,
736 self.NewExtentList((8, 128), (512, 6)))
737 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800738 PayloadError, payload_checker._CheckMoveOperation,
Gilad Arnold5502b562013-03-08 13:22:31 -0800739 op, None, 134, 134, 'foo')
740
Allie Woodb065e132015-04-24 10:20:27 -0700741 self.AddToMessage(op.src_extents,
742 self.NewExtentList((1, 4), (12, 2), (1024, 128)))
743 self.AddToMessage(op.dst_extents,
744 self.NewExtentList((0, 128), (512, 6)))
745 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800746 PayloadError, payload_checker._CheckMoveOperation,
Allie Woodb065e132015-04-24 10:20:27 -0700747 op, None, 134, 134, 'foo')
748
Sen Jiang92161a72016-06-28 16:09:38 -0700749 def testCheckAnyDiff(self):
750 """Tests _CheckAnyDiffOperation()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800751 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassaniefa62d92017-11-09 13:46:56 -0800752 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800753
754 # Pass.
755 self.assertIsNone(
Amin Hassaniefa62d92017-11-09 13:46:56 -0800756 payload_checker._CheckAnyDiffOperation(op, 10000, 3, 'foo'))
Gilad Arnold5502b562013-03-08 13:22:31 -0800757
758 # Fail, missing data blob.
759 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800760 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800761 op, None, 3, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800762
763 # Fail, too big of a diff blob (unjustified).
764 self.assertRaises(
Amin Hassanib05a65a2017-12-18 15:15:32 -0800765 PayloadError, payload_checker._CheckAnyDiffOperation,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800766 op, 10000, 2, 'foo')
Gilad Arnold5502b562013-03-08 13:22:31 -0800767
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800768 def testCheckSourceCopyOperation_Pass(self):
769 """Tests _CheckSourceCopyOperation(); pass case."""
770 payload_checker = checker.PayloadChecker(self.MockPayload())
771 self.assertIsNone(
772 payload_checker._CheckSourceCopyOperation(None, 134, 134, 'foo'))
773
774 def testCheckSourceCopyOperation_FailContainsData(self):
775 """Tests _CheckSourceCopyOperation(); message contains data."""
776 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800777 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800778 134, 0, 0, 'foo')
779
780 def testCheckSourceCopyOperation_FailBlockCountsMismatch(self):
781 """Tests _CheckSourceCopyOperation(); src and dst block totals not equal."""
782 payload_checker = checker.PayloadChecker(self.MockPayload())
Amin Hassanib05a65a2017-12-18 15:15:32 -0800783 self.assertRaises(PayloadError, payload_checker._CheckSourceCopyOperation,
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800784 None, 0, 1, 'foo')
785
Gilad Arnold5502b562013-03-08 13:22:31 -0800786 def DoCheckOperationTest(self, op_type_name, is_last, allow_signature,
787 allow_unhashed, fail_src_extents, fail_dst_extents,
788 fail_mismatched_data_offset_length,
789 fail_missing_dst_extents, fail_src_length,
790 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -0800791 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -0800792 """Parametric testing of _CheckOperation().
793
794 Args:
Amin Hassanib44f73b2017-12-15 17:45:49 +0000795 op_type_name: 'REPLACE', 'REPLACE_BZ', 'MOVE', 'BSDIFF', 'SOURCE_COPY',
796 'SOURCE_BSDIFF', BROTLI_BSDIFF or 'PUFFDIFF'.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700797 is_last: Whether we're testing the last operation in a sequence.
798 allow_signature: Whether we're testing a signature-capable operation.
799 allow_unhashed: Whether we're allowing to not hash the data.
800 fail_src_extents: Tamper with src extents.
801 fail_dst_extents: Tamper with dst extents.
802 fail_mismatched_data_offset_length: Make data_{offset,length}
803 inconsistent.
804 fail_missing_dst_extents: Do not include dst extents.
805 fail_src_length: Make src length inconsistent.
806 fail_dst_length: Make dst length inconsistent.
807 fail_data_hash: Tamper with the data blob hash.
808 fail_prev_data_offset: Make data space uses incontiguous.
Allie Wood7cf9f132015-02-26 14:28:19 -0800809 fail_bad_minor_version: Make minor version incompatible with op.
Gilad Arnold5502b562013-03-08 13:22:31 -0800810 """
811 op_type = _OpTypeByName(op_type_name)
812
813 # Create the test object.
814 payload = self.MockPayload()
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700815 payload_checker = checker.PayloadChecker(payload,
816 allow_unhashed=allow_unhashed)
Gilad Arnold5502b562013-03-08 13:22:31 -0800817 block_size = payload_checker.block_size
818
819 # Create auxiliary arguments.
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700820 old_part_size = test_utils.MiB(4)
821 new_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800822 old_block_counters = array.array(
823 'B', [0] * ((old_part_size + block_size - 1) / block_size))
824 new_block_counters = array.array(
825 'B', [0] * ((new_part_size + block_size - 1) / block_size))
826 prev_data_offset = 1876
827 blob_hash_counts = collections.defaultdict(int)
828
829 # Create the operation object for the test.
Alex Deymo28466772015-09-11 17:16:44 -0700830 op = update_metadata_pb2.InstallOperation()
Gilad Arnold5502b562013-03-08 13:22:31 -0800831 op.type = op_type
832
833 total_src_blocks = 0
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800834 if op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700835 common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800836 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Gilad Arnold5502b562013-03-08 13:22:31 -0800837 if fail_src_extents:
838 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700839 self.NewExtentList((1, 0)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800840 else:
841 self.AddToMessage(op.src_extents,
Allie Woodb065e132015-04-24 10:20:27 -0700842 self.NewExtentList((1, 16)))
Gilad Arnold5502b562013-03-08 13:22:31 -0800843 total_src_blocks = 16
844
Allie Wood7cf9f132015-02-26 14:28:19 -0800845 if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700846 payload_checker.minor_version = 0
Allie Wood7cf9f132015-02-26 14:28:19 -0800847 elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700848 payload_checker.minor_version = 2 if fail_bad_minor_version else 1
Allie Wood7cf9f132015-02-26 14:28:19 -0800849 elif op_type in (common.OpType.SOURCE_COPY, common.OpType.SOURCE_BSDIFF):
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700850 payload_checker.minor_version = 1 if fail_bad_minor_version else 2
Amin Hassanicdeb6e62017-10-11 10:15:11 -0700851 elif op_type in (common.OpType.ZERO, common.OpType.DISCARD,
Amin Hassaniefa62d92017-11-09 13:46:56 -0800852 common.OpType.PUFFDIFF, common.OpType.BROTLI_BSDIFF):
Amin Hassani8ad22ba2017-10-11 10:15:11 -0700853 payload_checker.minor_version = 3 if fail_bad_minor_version else 4
Allie Wood7cf9f132015-02-26 14:28:19 -0800854
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800855 if op_type not in (common.OpType.MOVE, common.OpType.SOURCE_COPY):
Gilad Arnold5502b562013-03-08 13:22:31 -0800856 if not fail_mismatched_data_offset_length:
857 op.data_length = 16 * block_size - 8
858 if fail_prev_data_offset:
859 op.data_offset = prev_data_offset + 16
860 else:
861 op.data_offset = prev_data_offset
862
863 fake_data = 'fake-data'.ljust(op.data_length)
864 if not (allow_unhashed or (is_last and allow_signature and
865 op_type == common.OpType.REPLACE)):
866 if not fail_data_hash:
867 # Create a valid data blob hash.
868 op.data_sha256_hash = hashlib.sha256(fake_data).digest()
869 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
870 fake_data)
Amin Hassaniefa62d92017-11-09 13:46:56 -0800871
Gilad Arnold5502b562013-03-08 13:22:31 -0800872 elif fail_data_hash:
873 # Create an invalid data blob hash.
874 op.data_sha256_hash = hashlib.sha256(
875 fake_data.replace(' ', '-')).digest()
876 payload.ReadDataBlob(op.data_offset, op.data_length).AndReturn(
877 fake_data)
878
879 total_dst_blocks = 0
880 if not fail_missing_dst_extents:
881 total_dst_blocks = 16
882 if fail_dst_extents:
883 self.AddToMessage(op.dst_extents,
884 self.NewExtentList((4, 16), (32, 0)))
885 else:
886 self.AddToMessage(op.dst_extents,
887 self.NewExtentList((4, 8), (64, 8)))
888
889 if total_src_blocks:
890 if fail_src_length:
891 op.src_length = total_src_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800892 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
893 common.OpType.SOURCE_BSDIFF) and
894 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800895 op.src_length = total_src_blocks * block_size
896 elif fail_src_length:
897 # Add an orphaned src_length.
898 op.src_length = 16
899
900 if total_dst_blocks:
901 if fail_dst_length:
902 op.dst_length = total_dst_blocks * block_size + 8
Amin Hassaniefa62d92017-11-09 13:46:56 -0800903 elif (op_type in (common.OpType.MOVE, common.OpType.BSDIFF,
904 common.OpType.SOURCE_BSDIFF) and
905 payload_checker.minor_version <= 3):
Gilad Arnold5502b562013-03-08 13:22:31 -0800906 op.dst_length = total_dst_blocks * block_size
907
908 self.mox.ReplayAll()
909 should_fail = (fail_src_extents or fail_dst_extents or
910 fail_mismatched_data_offset_length or
911 fail_missing_dst_extents or fail_src_length or
Allie Wood7cf9f132015-02-26 14:28:19 -0800912 fail_dst_length or fail_data_hash or fail_prev_data_offset or
913 fail_bad_minor_version)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700914 args = (op, 'foo', is_last, old_block_counters, new_block_counters,
915 old_part_size, new_part_size, prev_data_offset, allow_signature,
916 blob_hash_counts)
Gilad Arnold5502b562013-03-08 13:22:31 -0800917 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800918 self.assertRaises(PayloadError, payload_checker._CheckOperation, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800919 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700920 self.assertEqual(op.data_length if op.HasField('data_length') else 0,
921 payload_checker._CheckOperation(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800922
923 def testAllocBlockCounters(self):
924 """Tests _CheckMoveOperation()."""
925 payload_checker = checker.PayloadChecker(self.MockPayload())
926 block_size = payload_checker.block_size
927
928 # Check allocation for block-aligned partition size, ensure it's integers.
929 result = payload_checker._AllocBlockCounters(16 * block_size)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700930 self.assertEqual(16, len(result))
931 self.assertEqual(int, type(result[0]))
Gilad Arnold5502b562013-03-08 13:22:31 -0800932
933 # Check allocation of unaligned partition sizes.
934 result = payload_checker._AllocBlockCounters(16 * block_size - 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700935 self.assertEqual(16, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800936 result = payload_checker._AllocBlockCounters(16 * block_size + 1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700937 self.assertEqual(17, len(result))
Gilad Arnold5502b562013-03-08 13:22:31 -0800938
Allie Woodfb04d302015-04-03 14:25:48 -0700939 def DoCheckOperationsTest(self, fail_nonexhaustive_full_update):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800940 """Tests _CheckOperations()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800941 # Generate a test payload. For this test, we only care about one
942 # (arbitrary) set of operations, so we'll only be generating kernel and
943 # test with them.
944 payload_gen = test_utils.PayloadGenerator()
945
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700946 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800947 payload_gen.SetBlockSize(block_size)
948
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700949 rootfs_part_size = test_utils.MiB(8)
Gilad Arnold5502b562013-03-08 13:22:31 -0800950
951 # Fake rootfs operations in a full update, tampered with as required.
952 rootfs_op_type = common.OpType.REPLACE
Gilad Arnold5502b562013-03-08 13:22:31 -0800953 rootfs_data_length = rootfs_part_size
954 if fail_nonexhaustive_full_update:
955 rootfs_data_length -= block_size
956
957 payload_gen.AddOperation(False, rootfs_op_type,
958 dst_extents=[(0, rootfs_data_length / block_size)],
959 data_offset=0,
960 data_length=rootfs_data_length)
961
962 # Create the test object.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700963 payload_checker = _GetPayloadChecker(payload_gen.WriteToFile,
964 checker_init_dargs={
965 'allow_unhashed': True})
Gilad Arnold5502b562013-03-08 13:22:31 -0800966 payload_checker.payload_type = checker._TYPE_FULL
967 report = checker._PayloadReport()
968
Amin Hassaniae853742017-10-11 10:27:27 -0700969 args = (payload_checker.payload.manifest.install_operations, report, 'foo',
970 0, rootfs_part_size, rootfs_part_size, rootfs_part_size, 0, False)
Allie Woodfb04d302015-04-03 14:25:48 -0700971 if fail_nonexhaustive_full_update:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800972 self.assertRaises(PayloadError, payload_checker._CheckOperations, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -0800973 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700974 self.assertEqual(rootfs_data_length,
975 payload_checker._CheckOperations(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -0800976
977 def DoCheckSignaturesTest(self, fail_empty_sigs_blob, fail_missing_pseudo_op,
978 fail_mismatched_pseudo_op, fail_sig_missing_fields,
979 fail_unknown_sig_version, fail_incorrect_sig):
Amin Hassanib05a65a2017-12-18 15:15:32 -0800980 """Tests _CheckSignatures()."""
Gilad Arnold5502b562013-03-08 13:22:31 -0800981 # Generate a test payload. For this test, we only care about the signature
982 # block and how it relates to the payload hash. Therefore, we're generating
983 # a random (otherwise useless) payload for this purpose.
984 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700985 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -0800986 payload_gen.SetBlockSize(block_size)
Gilad Arnold18f4f9f2013-04-02 16:24:41 -0700987 rootfs_part_size = test_utils.MiB(2)
988 kernel_part_size = test_utils.KiB(16)
Gilad Arnold5502b562013-03-08 13:22:31 -0800989 payload_gen.SetPartInfo(False, True, rootfs_part_size,
990 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold382df5c2013-05-03 12:49:28 -0700991 payload_gen.SetPartInfo(True, True, kernel_part_size,
Gilad Arnold5502b562013-03-08 13:22:31 -0800992 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700993 payload_gen.SetMinorVersion(0)
Gilad Arnold5502b562013-03-08 13:22:31 -0800994 payload_gen.AddOperationWithData(
995 False, common.OpType.REPLACE,
996 dst_extents=[(0, rootfs_part_size / block_size)],
997 data_blob=os.urandom(rootfs_part_size))
998
999 do_forge_pseudo_op = (fail_missing_pseudo_op or fail_mismatched_pseudo_op)
1000 do_forge_sigs_data = (do_forge_pseudo_op or fail_empty_sigs_blob or
1001 fail_sig_missing_fields or fail_unknown_sig_version
1002 or fail_incorrect_sig)
1003
1004 sigs_data = None
1005 if do_forge_sigs_data:
1006 sigs_gen = test_utils.SignaturesGenerator()
1007 if not fail_empty_sigs_blob:
1008 if fail_sig_missing_fields:
1009 sig_data = None
1010 else:
1011 sig_data = test_utils.SignSha256('fake-payload-content',
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001012 test_utils._PRIVKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001013 sigs_gen.AddSig(5 if fail_unknown_sig_version else 1, sig_data)
1014
1015 sigs_data = sigs_gen.ToBinary()
1016 payload_gen.SetSignatures(payload_gen.curr_offset, len(sigs_data))
1017
1018 if do_forge_pseudo_op:
1019 assert sigs_data is not None, 'should have forged signatures blob by now'
1020 sigs_len = len(sigs_data)
1021 payload_gen.AddOperation(
1022 False, common.OpType.REPLACE,
1023 data_offset=payload_gen.curr_offset / 2,
1024 data_length=sigs_len / 2,
1025 dst_extents=[(0, (sigs_len / 2 + block_size - 1) / block_size)])
1026
1027 # Generate payload (complete w/ signature) and create the test object.
1028 payload_checker = _GetPayloadChecker(
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001029 payload_gen.WriteToFileWithData,
1030 payload_gen_dargs={
1031 'sigs_data': sigs_data,
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001032 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001033 'do_add_pseudo_operation': not do_forge_pseudo_op})
Gilad Arnold5502b562013-03-08 13:22:31 -08001034 payload_checker.payload_type = checker._TYPE_FULL
1035 report = checker._PayloadReport()
1036
1037 # We have to check the manifest first in order to set signature attributes.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001038 payload_checker._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold5502b562013-03-08 13:22:31 -08001039
1040 should_fail = (fail_empty_sigs_blob or fail_missing_pseudo_op or
1041 fail_mismatched_pseudo_op or fail_sig_missing_fields or
1042 fail_unknown_sig_version or fail_incorrect_sig)
Gilad Arnoldcb638912013-06-24 04:57:11 -07001043 args = (report, test_utils._PUBKEY_FILE_NAME)
Gilad Arnold5502b562013-03-08 13:22:31 -08001044 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001045 self.assertRaises(PayloadError, payload_checker._CheckSignatures, *args)
Gilad Arnold5502b562013-03-08 13:22:31 -08001046 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001047 self.assertIsNone(payload_checker._CheckSignatures(*args))
Gilad Arnold5502b562013-03-08 13:22:31 -08001048
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001049 def DoCheckManifestMinorVersionTest(self, minor_version, payload_type):
1050 """Parametric testing for CheckManifestMinorVersion().
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001051
1052 Args:
1053 minor_version: The payload minor version to test with.
1054 payload_type: The type of the payload we're testing, delta or full.
1055 """
1056 # Create the test object.
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001057 payload = self.MockPayload()
1058 payload.manifest.minor_version = minor_version
1059 payload_checker = checker.PayloadChecker(payload)
1060 payload_checker.payload_type = payload_type
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001061 report = checker._PayloadReport()
1062
1063 should_succeed = (
1064 (minor_version == 0 and payload_type == checker._TYPE_FULL) or
1065 (minor_version == 1 and payload_type == checker._TYPE_DELTA) or
Sen Jiang912c4df2015-12-10 12:17:13 -08001066 (minor_version == 2 and payload_type == checker._TYPE_DELTA) or
Sen Jiang92161a72016-06-28 16:09:38 -07001067 (minor_version == 3 and payload_type == checker._TYPE_DELTA) or
1068 (minor_version == 4 and payload_type == checker._TYPE_DELTA))
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001069 args = (report,)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001070
1071 if should_succeed:
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001072 self.assertIsNone(payload_checker._CheckManifestMinorVersion(*args))
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001073 else:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001074 self.assertRaises(PayloadError,
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001075 payload_checker._CheckManifestMinorVersion, *args)
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001076
Gilad Arnold06eea332015-07-13 18:06:33 -07001077 def DoRunTest(self, rootfs_part_size_provided, kernel_part_size_provided,
1078 fail_wrong_payload_type, fail_invalid_block_size,
1079 fail_mismatched_block_size, fail_excess_data,
1080 fail_rootfs_part_size_exceeded,
1081 fail_kernel_part_size_exceeded):
Amin Hassanib05a65a2017-12-18 15:15:32 -08001082 """Tests Run()."""
Gilad Arnold5502b562013-03-08 13:22:31 -08001083 # Generate a test payload. For this test, we generate a full update that
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001084 # has sample kernel and rootfs operations. Since most testing is done with
Gilad Arnold5502b562013-03-08 13:22:31 -08001085 # internal PayloadChecker methods that are tested elsewhere, here we only
1086 # tamper with what's actually being manipulated and/or tested in the Run()
1087 # method itself. Note that the checker doesn't verify partition hashes, so
1088 # they're safe to fake.
1089 payload_gen = test_utils.EnhancedPayloadGenerator()
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001090 block_size = test_utils.KiB(4)
Gilad Arnold5502b562013-03-08 13:22:31 -08001091 payload_gen.SetBlockSize(block_size)
Gilad Arnold06eea332015-07-13 18:06:33 -07001092 kernel_filesystem_size = test_utils.KiB(16)
1093 rootfs_filesystem_size = test_utils.MiB(2)
1094 payload_gen.SetPartInfo(False, True, rootfs_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001095 hashlib.sha256('fake-new-rootfs-content').digest())
Gilad Arnold06eea332015-07-13 18:06:33 -07001096 payload_gen.SetPartInfo(True, True, kernel_filesystem_size,
Gilad Arnold5502b562013-03-08 13:22:31 -08001097 hashlib.sha256('fake-new-kernel-content').digest())
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001098 payload_gen.SetMinorVersion(0)
Gilad Arnold06eea332015-07-13 18:06:33 -07001099
1100 rootfs_part_size = 0
1101 if rootfs_part_size_provided:
1102 rootfs_part_size = rootfs_filesystem_size + block_size
1103 rootfs_op_size = rootfs_part_size or rootfs_filesystem_size
1104 if fail_rootfs_part_size_exceeded:
1105 rootfs_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001106 payload_gen.AddOperationWithData(
1107 False, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001108 dst_extents=[(0, rootfs_op_size / block_size)],
1109 data_blob=os.urandom(rootfs_op_size))
1110
1111 kernel_part_size = 0
1112 if kernel_part_size_provided:
1113 kernel_part_size = kernel_filesystem_size + block_size
1114 kernel_op_size = kernel_part_size or kernel_filesystem_size
1115 if fail_kernel_part_size_exceeded:
1116 kernel_op_size += block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001117 payload_gen.AddOperationWithData(
1118 True, common.OpType.REPLACE,
Gilad Arnold06eea332015-07-13 18:06:33 -07001119 dst_extents=[(0, kernel_op_size / block_size)],
1120 data_blob=os.urandom(kernel_op_size))
Gilad Arnold5502b562013-03-08 13:22:31 -08001121
1122 # Generate payload (complete w/ signature) and create the test object.
Gilad Arnold5502b562013-03-08 13:22:31 -08001123 if fail_invalid_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001124 use_block_size = block_size + 5 # Not a power of two.
Gilad Arnold5502b562013-03-08 13:22:31 -08001125 elif fail_mismatched_block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001126 use_block_size = block_size * 2 # Different that payload stated.
Gilad Arnold5502b562013-03-08 13:22:31 -08001127 else:
1128 use_block_size = block_size
Gilad Arnold5502b562013-03-08 13:22:31 -08001129
Gilad Arnoldcb638912013-06-24 04:57:11 -07001130 kwargs = {
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001131 'payload_gen_dargs': {
Gilad Arnold18f4f9f2013-04-02 16:24:41 -07001132 'privkey_file_name': test_utils._PRIVKEY_FILE_NAME,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001133 'do_add_pseudo_operation': True,
1134 'is_pseudo_in_kernel': True,
1135 'padding': os.urandom(1024) if fail_excess_data else None},
1136 'checker_init_dargs': {
1137 'assert_type': 'delta' if fail_wrong_payload_type else 'full',
1138 'block_size': use_block_size}}
1139 if fail_invalid_block_size:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001140 self.assertRaises(PayloadError, _GetPayloadChecker,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001141 payload_gen.WriteToFileWithData, **kwargs)
Gilad Arnold5502b562013-03-08 13:22:31 -08001142 else:
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001143 payload_checker = _GetPayloadChecker(payload_gen.WriteToFileWithData,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001144 **kwargs)
Gilad Arnold06eea332015-07-13 18:06:33 -07001145
1146 kwargs = {'pubkey_file_name': test_utils._PUBKEY_FILE_NAME,
1147 'rootfs_part_size': rootfs_part_size,
1148 'kernel_part_size': kernel_part_size}
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001149 should_fail = (fail_wrong_payload_type or fail_mismatched_block_size or
Gilad Arnold06eea332015-07-13 18:06:33 -07001150 fail_excess_data or
1151 fail_rootfs_part_size_exceeded or
1152 fail_kernel_part_size_exceeded)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001153 if should_fail:
Amin Hassanib05a65a2017-12-18 15:15:32 -08001154 self.assertRaises(PayloadError, payload_checker.Run, **kwargs)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001155 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001156 self.assertIsNone(payload_checker.Run(**kwargs))
Gilad Arnold5502b562013-03-08 13:22:31 -08001157
Gilad Arnold5502b562013-03-08 13:22:31 -08001158# This implements a generic API, hence the occasional unused args.
1159# pylint: disable=W0613
1160def ValidateCheckOperationTest(op_type_name, is_last, allow_signature,
1161 allow_unhashed, fail_src_extents,
1162 fail_dst_extents,
1163 fail_mismatched_data_offset_length,
1164 fail_missing_dst_extents, fail_src_length,
1165 fail_dst_length, fail_data_hash,
Allie Wood7cf9f132015-02-26 14:28:19 -08001166 fail_prev_data_offset, fail_bad_minor_version):
Gilad Arnold5502b562013-03-08 13:22:31 -08001167 """Returns True iff the combination of arguments represents a valid test."""
1168 op_type = _OpTypeByName(op_type_name)
1169
Amin Hassanib44f73b2017-12-15 17:45:49 +00001170 # REPLACE/REPLACE_BZ operations don't read data from src partition. They are
1171 # compatible with all valid minor versions, so we don't need to check that.
1172 if (op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ) and (
1173 fail_src_extents or fail_src_length or fail_bad_minor_version)):
Gilad Arnold5502b562013-03-08 13:22:31 -08001174 return False
1175
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001176 # MOVE and SOURCE_COPY operations don't carry data.
1177 if (op_type in (common.OpType.MOVE, common.OpType.SOURCE_COPY) and (
Gilad Arnold5502b562013-03-08 13:22:31 -08001178 fail_mismatched_data_offset_length or fail_data_hash or
1179 fail_prev_data_offset)):
1180 return False
1181
1182 return True
1183
1184
1185def TestMethodBody(run_method_name, run_dargs):
1186 """Returns a function that invokes a named method with named arguments."""
1187 return lambda self: getattr(self, run_method_name)(**run_dargs)
1188
1189
1190def AddParametricTests(tested_method_name, arg_space, validate_func=None):
1191 """Enumerates and adds specific parametric tests to PayloadCheckerTest.
1192
1193 This function enumerates a space of test parameters (defined by arg_space),
1194 then binds a new, unique method name in PayloadCheckerTest to a test function
1195 that gets handed the said parameters. This is a preferable approach to doing
1196 the enumeration and invocation during the tests because this way each test is
1197 treated as a complete run by the unittest framework, and so benefits from the
1198 usual setUp/tearDown mechanics.
1199
1200 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001201 tested_method_name: Name of the tested PayloadChecker method.
1202 arg_space: A dictionary containing variables (keys) and lists of values
1203 (values) associated with them.
1204 validate_func: A function used for validating test argument combinations.
Gilad Arnold5502b562013-03-08 13:22:31 -08001205 """
1206 for value_tuple in itertools.product(*arg_space.itervalues()):
1207 run_dargs = dict(zip(arg_space.iterkeys(), value_tuple))
1208 if validate_func and not validate_func(**run_dargs):
1209 continue
1210 run_method_name = 'Do%sTest' % tested_method_name
1211 test_method_name = 'test%s' % tested_method_name
1212 for arg_key, arg_val in run_dargs.iteritems():
1213 if arg_val or type(arg_val) is int:
1214 test_method_name += '__%s=%s' % (arg_key, arg_val)
1215 setattr(PayloadCheckerTest, test_method_name,
1216 TestMethodBody(run_method_name, run_dargs))
1217
1218
1219def AddAllParametricTests():
1220 """Enumerates and adds all parametric tests to PayloadCheckerTest."""
1221 # Add all _CheckElem() test cases.
1222 AddParametricTests('AddElem',
1223 {'linebreak': (True, False),
1224 'indent': (0, 1, 2),
1225 'convert': (str, lambda s: s[::-1]),
1226 'is_present': (True, False),
1227 'is_mandatory': (True, False),
1228 'is_submsg': (True, False)})
1229
1230 # Add all _Add{Mandatory,Optional}Field tests.
1231 AddParametricTests('AddField',
1232 {'is_mandatory': (True, False),
1233 'linebreak': (True, False),
1234 'indent': (0, 1, 2),
1235 'convert': (str, lambda s: s[::-1]),
1236 'is_present': (True, False)})
1237
1238 # Add all _Add{Mandatory,Optional}SubMsg tests.
1239 AddParametricTests('AddSubMsg',
1240 {'is_mandatory': (True, False),
1241 'is_present': (True, False)})
1242
1243 # Add all _CheckManifest() test cases.
1244 AddParametricTests('CheckManifest',
1245 {'fail_mismatched_block_size': (True, False),
1246 'fail_bad_sigs': (True, False),
1247 'fail_mismatched_oki_ori': (True, False),
1248 'fail_bad_oki': (True, False),
1249 'fail_bad_ori': (True, False),
1250 'fail_bad_nki': (True, False),
1251 'fail_bad_nri': (True, False),
Gilad Arnold382df5c2013-05-03 12:49:28 -07001252 'fail_old_kernel_fs_size': (True, False),
1253 'fail_old_rootfs_fs_size': (True, False),
1254 'fail_new_kernel_fs_size': (True, False),
1255 'fail_new_rootfs_fs_size': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001256
1257 # Add all _CheckOperation() test cases.
1258 AddParametricTests('CheckOperation',
Amin Hassanib44f73b2017-12-15 17:45:49 +00001259 {'op_type_name': ('REPLACE', 'REPLACE_BZ', 'MOVE',
1260 'BSDIFF', 'SOURCE_COPY',
Amin Hassaniefa62d92017-11-09 13:46:56 -08001261 'SOURCE_BSDIFF', 'PUFFDIFF',
1262 'BROTLI_BSDIFF'),
Gilad Arnold5502b562013-03-08 13:22:31 -08001263 'is_last': (True, False),
1264 'allow_signature': (True, False),
1265 'allow_unhashed': (True, False),
1266 'fail_src_extents': (True, False),
1267 'fail_dst_extents': (True, False),
1268 'fail_mismatched_data_offset_length': (True, False),
1269 'fail_missing_dst_extents': (True, False),
1270 'fail_src_length': (True, False),
1271 'fail_dst_length': (True, False),
1272 'fail_data_hash': (True, False),
Allie Wood7cf9f132015-02-26 14:28:19 -08001273 'fail_prev_data_offset': (True, False),
1274 'fail_bad_minor_version': (True, False)},
Gilad Arnold5502b562013-03-08 13:22:31 -08001275 validate_func=ValidateCheckOperationTest)
1276
1277 # Add all _CheckOperations() test cases.
1278 AddParametricTests('CheckOperations',
Allie Woodfb04d302015-04-03 14:25:48 -07001279 {'fail_nonexhaustive_full_update': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001280
1281 # Add all _CheckOperations() test cases.
1282 AddParametricTests('CheckSignatures',
1283 {'fail_empty_sigs_blob': (True, False),
1284 'fail_missing_pseudo_op': (True, False),
1285 'fail_mismatched_pseudo_op': (True, False),
1286 'fail_sig_missing_fields': (True, False),
1287 'fail_unknown_sig_version': (True, False),
1288 'fail_incorrect_sig': (True, False)})
1289
Gilad Arnold0d575cd2015-07-13 17:29:21 -07001290 # Add all _CheckManifestMinorVersion() test cases.
1291 AddParametricTests('CheckManifestMinorVersion',
Sen Jiang92161a72016-06-28 16:09:38 -07001292 {'minor_version': (None, 0, 1, 2, 3, 4, 555),
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001293 'payload_type': (checker._TYPE_FULL,
1294 checker._TYPE_DELTA)})
1295
Gilad Arnold5502b562013-03-08 13:22:31 -08001296 # Add all Run() test cases.
1297 AddParametricTests('Run',
Gilad Arnold06eea332015-07-13 18:06:33 -07001298 {'rootfs_part_size_provided': (True, False),
1299 'kernel_part_size_provided': (True, False),
1300 'fail_wrong_payload_type': (True, False),
Gilad Arnold5502b562013-03-08 13:22:31 -08001301 'fail_invalid_block_size': (True, False),
1302 'fail_mismatched_block_size': (True, False),
Gilad Arnold06eea332015-07-13 18:06:33 -07001303 'fail_excess_data': (True, False),
1304 'fail_rootfs_part_size_exceeded': (True, False),
1305 'fail_kernel_part_size_exceeded': (True, False)})
Gilad Arnold5502b562013-03-08 13:22:31 -08001306
1307
1308if __name__ == '__main__':
1309 AddAllParametricTests()
1310 unittest.main()