blob: 6e8aea67abd4438f9646bcad9307087efde1862a [file] [log] [blame]
Mike Frysinger2daf0412019-07-12 23:38:16 -04001#!/usr/bin/env python3
Evan Green22bae082019-06-18 15:58:24 -07002# -*- coding: utf-8 -*-
Alex Deymo7db4b4b2015-01-30 11:00:06 -08003# Copyright 2015 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 tests for cgpt."""
8
9# pylint: disable=W0212
10
11from __future__ import print_function
12
Alex Deymo7db4b4b2015-01-30 11:00:06 -080013import os
14import shutil
15import tempfile
16import unittest
17
Mike Frysinger5175e872019-08-26 14:15:53 -040018import cgpt
19
Alex Deymo7db4b4b2015-01-30 11:00:06 -080020
21class JSONLoadingTest(unittest.TestCase):
22 """Test stacked JSON loading functions."""
23
24 def __init__(self, *args, **kwargs):
25 unittest.TestCase.__init__(self, *args, **kwargs)
26 self.tempdir = None
27 self.maxDiff = 1000
28
29 def setUp(self):
30 self.tempdir = tempfile.mkdtemp(prefix='cgpt-test_')
31 self.layout_json = os.path.join(self.tempdir, 'test_layout.json')
32 self.parent_layout_json = os.path.join(self.tempdir,
33 'test_layout_parent.json')
34
35 def tearDown(self):
36 if self.tempdir is not None:
37 shutil.rmtree(self.tempdir)
38 self.tempdir = None
39
40 def testJSONComments(self):
41 """Test that we ignore comments in JSON in lines starting with #."""
42 with open(self.layout_json, 'w') as f:
43 f.write("""# This line is a comment.
44{
45 # Here I have another comment starting with some whitespaces on the left.
46 "layouts": {
47 "common": []
48 }
49}
50""")
51 self.assertEqual(cgpt._LoadStackedPartitionConfig(self.layout_json),
52 {'layouts': {'common': []}})
53
54 def testJSONCommentsLimitations(self):
55 """Test that we can't parse inline comments in JSON.
56
57 If we ever enable this, we need to change the README.disk_layout
58 documentation to mention it.
59 """
60 with open(self.layout_json, 'w') as f:
61 f.write("""{
62 "layouts": { # This is an inline comment, but is not supported.
63 "common": []}}""")
64 self.assertRaises(ValueError,
65 cgpt._LoadStackedPartitionConfig, self.layout_json)
66
67 def testPartitionOrderPreserved(self):
68 """Test that the order of the partitions is the same as in the parent."""
69 with open(self.parent_layout_json, 'w') as f:
70 f.write("""{
71 "layouts": {
72 "common": [
73 {
74 "num": 3,
75 "name": "Part 3"
76 },
77 {
78 "num": 2,
79 "name": "Part 2"
80 },
81 {
82 "num": 1,
83 "name": "Part 1"
84 }
85 ]
86 }
87}""")
88 parent_layout = cgpt._LoadStackedPartitionConfig(self.parent_layout_json)
89
90 with open(self.layout_json, 'w') as f:
91 f.write("""{
92 "parent": "%s",
93 "layouts": {
94 "common": []
95 }
96}""" % self.parent_layout_json)
97 layout = cgpt._LoadStackedPartitionConfig(self.layout_json)
98 self.assertEqual(parent_layout, layout)
99
100 # Test also that even overriding one partition keeps all of them in order.
101 with open(self.layout_json, 'w') as f:
102 f.write("""{
103 "parent": "%s",
104 "layouts": {
105 "common": [
106 {
107 "num": 2,
108 "name": "Part 2"
109 }
110 ]
111 }
112}""" % self.parent_layout_json)
113 layout = cgpt._LoadStackedPartitionConfig(self.layout_json)
114 self.assertEqual(parent_layout, layout)
115
Evan Green22bae082019-06-18 15:58:24 -0700116 def testGetStartByteOffsetIsAccurate(self):
117 """Test that padding_bytes results in a valid start sector."""
118
Joshua Emele45cef372017-09-15 16:15:29 -0700119 test_params = (
Evan Green22bae082019-06-18 15:58:24 -0700120 # block_size, primary_entry_array_padding_bytes (in blocks)
121 (512, 2),
122 (512, 32768),
123 (1024, 32768),
Joshua Emele45cef372017-09-15 16:15:29 -0700124 )
125 for i in test_params:
126 with open(self.layout_json, 'w') as f:
127 f.write("""{
128 "metadata": {
129 "block_size": %d,
130 "fs_block_size": 4096,
Evan Green22bae082019-06-18 15:58:24 -0700131 "primary_entry_array_padding_bytes": %d
Joshua Emele45cef372017-09-15 16:15:29 -0700132 },
133 "layouts": {
134 "base": [
135 {
136 "type": "blank",
137 "size": "32 MiB"
138 }
139 ]
140 }
Evan Green22bae082019-06-18 15:58:24 -0700141}""" % (i[0], i[1] * i[0]))
Joshua Emele45cef372017-09-15 16:15:29 -0700142
143 config = cgpt.LoadPartitionConfig(self.layout_json)
144 class Options(object):
Evan Green22bae082019-06-18 15:58:24 -0700145 """Fake options"""
Joshua Emele45cef372017-09-15 16:15:29 -0700146 adjust_part = ''
147 partitions = cgpt.GetPartitionTable(Options(), config, 'base')
Evan Green22bae082019-06-18 15:58:24 -0700148 start_offset = cgpt._GetPartitionStartByteOffset(config, partitions)
149 self.assertEqual(start_offset, cgpt.START_SECTOR + i[1] * i[0])
Joshua Emele45cef372017-09-15 16:15:29 -0700150
Evan Green22bae082019-06-18 15:58:24 -0700151 def testGetTableTotalsSizeIsAccurate(self):
Joshua Emele45cef372017-09-15 16:15:29 -0700152 """Test that primary_entry_array_lba results in an accurate block count."""
153 test_params = (
Evan Green22bae082019-06-18 15:58:24 -0700154 # block_size, primary_entry_array_padding_bytes (in blocks),
155 # partition size (MiB)
156 (512, 2, 32),
157 (1024, 2, 32),
158 (512, 2, 64),
159 (512, 32768, 32),
Joshua Emele45cef372017-09-15 16:15:29 -0700160 (1024, 32768, 32),
161 (1024, 32768, 64),
162 )
163 for i in test_params:
164 with open(self.layout_json, 'w') as f:
165 f.write("""{
166 "metadata": {
167 "block_size": %d,
168 "fs_block_size": 4096,
Evan Green22bae082019-06-18 15:58:24 -0700169 "primary_entry_array_padding_bytes": %d
Joshua Emele45cef372017-09-15 16:15:29 -0700170 },
171 "layouts": {
172 "base": [
173 {
174 "type": "blank",
175 "size": "%d MiB"
176 }
177 ]
178 }
Evan Green22bae082019-06-18 15:58:24 -0700179}""" % (i[0], i[1] * i[0], i[2]))
Joshua Emele45cef372017-09-15 16:15:29 -0700180
181 config = cgpt.LoadPartitionConfig(self.layout_json)
182 class Options(object):
Evan Green22bae082019-06-18 15:58:24 -0700183 """Fake options"""
Joshua Emele45cef372017-09-15 16:15:29 -0700184 adjust_part = ''
185 partitions = cgpt.GetPartitionTable(Options(), config, 'base')
186 totals = cgpt.GetTableTotals(config, partitions)
187
188 # Calculate the expected image block size.
Evan Green22bae082019-06-18 15:58:24 -0700189 total_size = (
190 cgpt._GetPartitionStartByteOffset(config, partitions) +
191 sum([x['bytes'] for x in partitions]) +
192 cgpt.SECONDARY_GPT_BYTES)
193
194 self.assertEqual(totals['byte_count'], total_size)
Joshua Emele45cef372017-09-15 16:15:29 -0700195
Alex Deymo7db4b4b2015-01-30 11:00:06 -0800196 def testGapPartitionsAreIncluded(self):
197 """Test that empty partitions (gaps) can be included in the child layout."""
198 with open(self.layout_json, 'w') as f:
199 f.write("""{
200 "layouts": {
201 # The common layout is empty but is applied to all the other layouts.
202 "common": [],
203 "base": [
204 {
205 "num": 2,
206 "name": "Part 2"
207 },
208 {
209 # Pad out, but not sure why.
210 "type": "blank",
211 "size": "64 MiB"
212 },
213 {
214 "num": 1,
215 "name": "Part 1"
216 }
217 ]
218 }
219}""")
220 self.assertEqual(
221 cgpt._LoadStackedPartitionConfig(self.layout_json),
222 {
223 'layouts': {
224 'common': [],
225 'base': [
Mike Frysinger5175e872019-08-26 14:15:53 -0400226 {'num': 2, 'name': 'Part 2'},
227 {'type': 'blank', 'size': '64 MiB'},
228 {'num': 1, 'name': 'Part 1'}
Alex Deymo7db4b4b2015-01-30 11:00:06 -0800229 ]
230 }})
231
232 def testPartitionOrderShouldMatch(self):
233 """Test that the partition order in parent and child layouts must match."""
234 with open(self.layout_json, 'w') as f:
235 f.write("""{
236 "layouts": {
237 "common": [
238 {"num": 1},
239 {"num": 2}
240 ],
241 "base": [
242 {"num": 2},
243 {"num": 1}
244 ]
245 }
246}""")
247 self.assertRaises(cgpt.ConflictingPartitionOrder,
248 cgpt._LoadStackedPartitionConfig, self.layout_json)
249
250 def testOnlySharedPartitionsOrderMatters(self):
251 """Test that only the order of the partition in both layouts matters."""
252 with open(self.layout_json, 'w') as f:
253 f.write("""{
254 "layouts": {
255 "common": [
256 {"num": 1},
257 {"num": 2},
258 {"num": 3}
259 ],
260 "base": [
261 {"num": 2},
262 {"num": 12},
263 {"num": 3},
264 {"num": 5}
265 ]
266 }
267}""")
268 self.assertEqual(
269 cgpt._LoadStackedPartitionConfig(self.layout_json),
270 {
271 'layouts': {
272 'common': [
273 {'num': 1},
274 {'num': 2},
275 {'num': 3}
276 ],
277 'base': [
278 {'num': 1},
279 {'num': 2},
280 {'num': 12},
281 {'num': 3},
282 {'num': 5}
283 ]
284 }})
285
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800286 def testFileSystemSizeMustBePositive(self):
287 """Test that zero or negative file system size will raise exception."""
288 with open(self.layout_json, 'w') as f:
289 f.write("""{
290 "metadata": {
291 "block_size": "512",
292 "fs_block_size": "4 KiB"
293 },
294 "layouts": {
295 "base": [
296 {
297 "num": 1,
298 "type": "rootfs",
299 "label": "ROOT-A",
300 "fs_size": "0 KiB"
301 }
302 ]
303 }
304}""")
305 try:
306 cgpt.LoadPartitionConfig(self.layout_json)
307 except cgpt.InvalidSize as e:
308 self.assertTrue('must be positive' in str(e))
309 else:
310 self.fail('InvalidSize not raised.')
311
312 def testFileSystemSizeLargerThanPartition(self):
313 """Test that file system size must not be greater than partition."""
314 with open(self.layout_json, 'w') as f:
315 f.write("""{
316 "metadata": {
317 "block_size": "512",
318 "fs_block_size": "4 KiB"
319 },
320 "layouts": {
321 "base": [
322 {
323 "num": 1,
324 "type": "rootfs",
325 "label": "ROOT-A",
326 "size": "4 KiB",
327 "fs_size": "8 KiB"
328 }
329 ]
330 }
331}""")
332 try:
333 cgpt.LoadPartitionConfig(self.layout_json)
334 except cgpt.InvalidSize as e:
335 self.assertTrue('may not be larger than partition' in str(e))
336 else:
337 self.fail('InvalidSize not raised.')
338
339 def testFileSystemSizeNotMultipleBlocks(self):
340 """Test that file system size must be multiples of file system blocks."""
341 with open(self.layout_json, 'w') as f:
342 f.write("""{
343 "metadata": {
344 "block_size": "512",
345 "fs_block_size": "4 KiB"
346 },
347 "layouts": {
348 "base": [
349 {
350 "num": 1,
351 "type": "rootfs",
352 "label": "ROOT-A",
353 "size": "4 KiB",
354 "fs_size": "3 KiB"
355 }
356 ]
357 }
358}""")
359 try:
360 cgpt.LoadPartitionConfig(self.layout_json)
361 except cgpt.InvalidSize as e:
Evan Green22bae082019-06-18 15:58:24 -0700362 self.assertTrue('not an even multiple of fs_align' in str(e))
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800363 else:
364 self.fail('InvalidSize not raised.')
365
366 def testFileSystemSizeForUbiWithNoPageSize(self):
367 """Test that "page_size" must be present to calculate UBI fs size."""
368 with open(self.layout_json, 'w') as f:
369 f.write("""{
370 "metadata": {
371 "block_size": "512",
372 "fs_block_size": "4 KiB"
373 },
374 "layouts": {
375 "base": [
376 {
377 "num": 1,
378 "type": "rootfs",
379 "format": "ubi",
380 "label": "ROOT-A",
381 "size": "4 KiB",
382 "fs_size": "4 KiB"
383 }
384 ]
385 }
386}""")
387 try:
388 cgpt.LoadPartitionConfig(self.layout_json)
389 except cgpt.InvalidLayout as e:
390 self.assertTrue('page_size' in str(e))
391 else:
392 self.fail('InvalidLayout not raised.')
393
394 def testFileSystemSizeForUbiWithNoEraseBlockSize(self):
395 """Test that "erase_block_size" must be present to calculate UBI fs size."""
396 with open(self.layout_json, 'w') as f:
397 f.write("""{
398 "metadata": {
399 "block_size": "512",
400 "fs_block_size": "4 KiB"
401 },
402 "layouts": {
403 "base": [
404 {
405 "num": "metadata",
406 "page_size": "4 KiB"
407 },
408 {
409 "num": 1,
410 "type": "rootfs",
411 "format": "ubi",
412 "label": "ROOT-A",
413 "size": "4 KiB",
414 "fs_size": "4 KiB"
415 }
416 ]
417 }
418}""")
419 try:
420 cgpt.LoadPartitionConfig(self.layout_json)
421 except cgpt.InvalidLayout as e:
422 self.assertTrue('erase_block_size' in str(e))
423 else:
424 self.fail('InvalidLayout not raised.')
425
426 def testFileSystemSizeForUbiIsNotMultipleOfUbiEraseBlockSize(self):
427 """Test that we raise when fs_size is not multiple of eraseblocks."""
428 with open(self.layout_json, 'w') as f:
429 f.write("""{
430 "metadata": {
431 "block_size": "512",
432 "fs_block_size": "4 KiB"
433 },
434 "layouts": {
435 "base": [
436 {
437 "num": "metadata",
438 "page_size": "4 KiB",
439 "erase_block_size": "262144"
440 },
441 {
442 "num": 1,
443 "type": "rootfs",
444 "format": "ubi",
445 "label": "ROOT-A",
446 "size": "256 KiB",
447 "fs_size": "256 KiB"
448 }
449 ]
450 }
451}""")
452 try:
453 cgpt.LoadPartitionConfig(self.layout_json)
454 except cgpt.InvalidSize as e:
455 self.assertTrue('to "248 KiB" in the "common" layout' in str(e))
456 else:
457 self.fail('InvalidSize not raised')
458
459 def testFileSystemSizeForUbiIsMultipleOfUbiEraseBlockSize(self):
460 """Test that everything is okay when fs_size is multiple of eraseblocks."""
461 with open(self.layout_json, 'w') as f:
462 f.write("""{
463 "metadata": {
464 "block_size": "512",
465 "fs_block_size": "4 KiB"
466 },
467 "layouts": {
468 "base": [
469 {
470 "num": "metadata",
471 "page_size": "4 KiB",
472 "erase_block_size": "262144"
473 },
474 {
475 "num": 1,
476 "type": "rootfs",
477 "format": "ubi",
478 "label": "ROOT-A",
479 "size": "256 KiB",
480 "fs_size": "253952"
481 }
482 ]
483 }
484}""")
485 self.assertEqual(
486 cgpt.LoadPartitionConfig(self.layout_json),
487 {
Evan Green22bae082019-06-18 15:58:24 -0700488 u'layouts': {
489 u'base': [
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800490 {
Evan Green22bae082019-06-18 15:58:24 -0700491 u'erase_block_size': 262144,
Mike Frysinger97447572017-02-14 16:51:35 -0500492 'features': [],
Evan Green22bae082019-06-18 15:58:24 -0700493 u'num': u'metadata',
494 u'page_size': 4096,
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800495 'type': 'blank'
496 },
497 {
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800498 'bytes': 262144,
Mike Frysinger97447572017-02-14 16:51:35 -0500499 'features': [],
Evan Green22bae082019-06-18 15:58:24 -0700500 u'format': u'ubi',
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800501 'fs_bytes': 253952,
Mike Frysinger5175e872019-08-26 14:15:53 -0400502 u'fs_size': u'253952',
Evan Green22bae082019-06-18 15:58:24 -0700503 u'label': u'ROOT-A',
504 u'num': 1,
505 u'size': u'256 KiB',
506 u'type': u'rootfs'
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800507 }
508 ],
509 'common': []
510 },
Evan Green22bae082019-06-18 15:58:24 -0700511 u'metadata': {
512 u'block_size': u'512',
513 'fs_align': 4096,
514 u'fs_block_size': 4096
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800515 }
516 })
517
518
519class UtilityTest(unittest.TestCase):
520 """Test various utility functions in cgpt.py."""
521
522 def testParseHumanNumber(self):
523 """Test that ParseHumanNumber is correct."""
524 test_cases = [
Mike Frysinger5175e872019-08-26 14:15:53 -0400525 ('1', 1),
526 ('2', 2),
527 ('1KB', 1000),
528 ('1KiB', 1024),
529 ('1 K', 1024),
530 ('1 KiB', 1024),
531 ('3 MB', 3000000),
532 ('4 MiB', 4 * 2**20),
533 ('5GB', 5 * 10**9),
534 ('6GiB', 6 * 2**30),
535 ('7TB', 7 * 10**12),
536 ('8TiB', 8 * 2**40),
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800537 ]
538 for inp, exp in test_cases:
539 self.assertEqual(cgpt.ParseHumanNumber(inp), exp)
540
541 def testProduceHumanNumber(self):
542 """Test that ProduceHumanNumber is correct."""
543 test_cases = [
Mike Frysinger5175e872019-08-26 14:15:53 -0400544 ('1', 1),
545 ('2', 2),
546 ('1 KB', 1000),
547 ('1 KiB', 1024),
548 ('3 MB', 3 * 10**6),
549 ('4 MiB', 4 * 2**20),
550 ('5 GB', 5 * 10**9),
551 ('6 GiB', 6 * 2**30),
552 ('7 TB', 7 * 10**12),
553 ('8 TiB', 8 * 2**40),
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800554 ]
555 for exp, inp in test_cases:
556 self.assertEqual(cgpt.ProduceHumanNumber(inp), exp)
557
Mike Frysinger2daf0412019-07-12 23:38:16 -0400558 def testGetScriptShell(self):
559 """Verify GetScriptShell works."""
560 data = cgpt.GetScriptShell()
561 self.assertIn('#!/bin/sh', data)
Nam T. Nguyenc77ef592015-03-02 15:48:59 -0800562
563 def testParseProduce(self):
564 """Test that ParseHumanNumber(ProduceHumanNumber()) yields same value."""
565 test_cases = [
566 1, 2,
567 1000, 1024,
568 2 * 10**6, 2 * 2**20,
569 3 * 10**9, 3 * 2**30,
570 4 * 10**12, 4 * 2**40
571 ]
572 for n in test_cases:
573 self.assertEqual(cgpt.ParseHumanNumber(cgpt.ProduceHumanNumber(n)), n)
574
Alex Deymo7db4b4b2015-01-30 11:00:06 -0800575
576if __name__ == '__main__':
577 unittest.main()