Add 'position' attribute to influence the sequence of partitions.

This makes it possible to put vendor partitions in front of the
pre-defined Brillo partitions. This may be needed by some boot loaders.

Also make it permissible to omit the 'type_guid' attribute by defaulting
to 'brillo_vendor_specific' if omitted.

Slightly rework FakeGuidGenerator to ensure that we get stable GUIDs for
A/B partitions when feeding its output back into itself (idempotence).

TEST=New unit test + unit tests pass.
BUG=28252419

Change-Id: I7a8e18449a5f16105f429abf0fb6114a3b358fea
diff --git a/README b/README
index b61bff2..b4e6049 100644
--- a/README
+++ b/README
@@ -100,7 +100,8 @@
                 linux_fs
                 ms_basic_data
 
-              for well-known GPT partition GUIDs.
+              for well-known GPT partition GUIDs. If unset, the value
+              'brillo_vendor_specific' is used.
 
  flags:       A 64-bit integer (decimal or hexadecimal representations are
               accepted) for GPT flags. Default value is 0.
@@ -114,6 +115,12 @@
  ab_expanded: Set to 'true' only if this partition has already been
               expanded for A/B. Default value is 'false'.
 
+ position:    The position for the partition, used to determine the sequence
+              of partitions (and, indirectly, partition numbers) or 0 if
+              position doesn't matter. Partitions with low 'position'
+              numbers will be laid out before partitions with high
+              'position' numbers. Default value is 0.
+
 For key/value-pairs involving sizes, either integers can be used, e.g.
 
  "size": 1048576
diff --git a/bpt_unittest.py b/bpt_unittest.py
index 700573f..c2aa539 100755
--- a/bpt_unittest.py
+++ b/bpt_unittest.py
@@ -33,19 +33,10 @@
   is generating random GUIDs as per RFC 4122.
   """
 
-  def __init__(self):
-    """Initializes the object."""
-    self._number = 1
-
-  def reset(self):
-    """Resets the generator to its starting state."""
-    self._number = 1
-
-  def dispense_guid(self):
+  def dispense_guid(self, partition_number):
     """Dispenses a new GUID."""
 
-    uuid = '01234567-89ab-cdef-0123-%012x' % self._number
-    self._number += 1
+    uuid = '01234567-89ab-cdef-0123-%012x' % partition_number
     return uuid
 
 
@@ -171,7 +162,6 @@
     # just generated without passing any options (e.g. disk size,
     # alignment, suffixes). This verifies that we include all
     # necessary information in the generated JSON.
-    self.fake_guid_generator.reset()
     (json_str2, _) = bpt.make_table(
         [open(expected_json_file_name, 'r')],
         guid_generator=self.fake_guid_generator)
@@ -264,6 +254,13 @@
                      'test/change_system_size.bpt'],
                     'test/expected_json_stacked_change_ab_size.bpt')
 
+  def testPositionAttribute(self):
+    """Checks that it's possible to influence partition order."""
+    self._MakeTable(['test/base.bpt',
+                     'test/positions.bpt'],
+                    'test/expected_json_stacked_positions.bpt',
+                    disk_size=bpttool.ParseSize('10 GiB'))
+
   def testBinaryOutput(self):
     """Checks binary partition table output.
 
diff --git a/bpttool b/bpttool
index a6e6495..1f1bfef 100755
--- a/bpttool
+++ b/bpttool
@@ -46,6 +46,7 @@
 JSON_KEYWORD_PARTITIONS_IGNORE = 'ignore'
 JSON_KEYWORD_PARTITIONS_AB = 'ab'
 JSON_KEYWORD_PARTITIONS_AB_EXPANDED = 'ab_expanded'
+JSON_KEYWORD_PARTITIONS_POSITION = 'position'
 JSON_KEYWORD_AUTO = 'auto'
 
 # Possible values for the --type option of the query_partition
@@ -63,6 +64,9 @@
 
 GPT_NUM_LBAS = 33
 
+GPT_MIN_PART_NUM = 1
+GPT_MAX_PART_NUM = 128
+
 KNOWN_TYPE_GUIDS = {
     'brillo_boot': 'bb499290-b57e-49f6-bf41-190386693794',
     'brillo_system': '0f2778c4-5cc1-4300-8670-6c88b7e57ed6',
@@ -196,9 +200,13 @@
   directly using the uuid module.
   """
 
-  def dispense_guid(self):
+  def dispense_guid(self, partition_number):
     """Dispenses a GUID.
 
+    Arguments:
+      partition_number: The partition number or 0 if requesting a GUID
+                        for the whole disk.
+
     Returns:
       A RFC 4122 compliant GUID, as a string.
     """
@@ -221,6 +229,7 @@
     ab: If True, the partition is an A/B partition.
     ab_expanded: If True, the A/B partitions have been generated.
     ignore: If True, the partition should not be included in the final output.
+    position: The requested position of the partition or 0 if it doesn't matter.
   """
 
   def __init__(self):
@@ -235,6 +244,7 @@
     self.ab = False
     self.ab_expanded = False
     self.ignore = False
+    self.position = 0
 
   def add_info(self, pobj):
     """Add information to partition.
@@ -272,15 +282,21 @@
     value = pobj.get(JSON_KEYWORD_PARTITIONS_FLAGS)
     if value:
       self.flags = ParseNumber(value)
+    value = pobj.get(JSON_KEYWORD_PARTITIONS_POSITION)
+    if value:
+      self.position = ParseNumber(value)
 
-  def expand_guid(self, guid_generator):
-    """Assign instance GUID if required.
+  def expand_guid(self, guid_generator, partition_number):
+    """Assign instance GUID and type GUID if required.
 
     Arguments:
       guid_generator: A GuidGenerator object.
+      partition_number: The partition number, starting from 1.
     """
     if not self.guid or self.guid == JSON_KEYWORD_AUTO:
-      self.guid = guid_generator.dispense_guid()
+      self.guid = guid_generator.dispense_guid(partition_number)
+    if not self.type_guid:
+      self.type_guid = KNOWN_TYPE_GUIDS['brillo_vendor_specific']
 
   def validate(self):
     """Sanity checks data in object."""
@@ -303,6 +319,16 @@
       if not self.grow:
         raise ValueError('Size can only be unset if "grow" is True.')
 
+  def cmp(self, other):
+    """Comparison method."""
+    self_position = self.position
+    if self_position == 0:
+      self_position = GPT_MAX_PART_NUM
+    other_position = other.position
+    if other_position == 0:
+      other_position = GPT_MAX_PART_NUM
+    return cmp(self_position, other_position)
+
 
 class Settings(object):
   """An object for holding settings.
@@ -402,7 +428,7 @@
         for pobj in pobjs:
           if ab_collapse and pobj.get(JSON_KEYWORD_PARTITIONS_AB_EXPANDED):
             # If we encounter an expanded partition, unexpand it. This
-            # is make it possible to use output-JSON (from this tool)
+            # is to make it possible to use output-JSON (from this tool)
             # and stack it with an input-JSON file that e.g. specifies
             # size='256 GiB' for the 'system' partition.
             label = pobj[JSON_KEYWORD_PARTITIONS_LABEL]
@@ -470,7 +496,8 @@
               '      "' + JSON_KEYWORD_PARTITIONS_FLAGS + '": "{:#018x}",\n'
               '      "' + JSON_KEYWORD_PARTITIONS_IGNORE + '": {},\n'
               '      "' + JSON_KEYWORD_PARTITIONS_AB + '": {},\n'
-              '      "' + JSON_KEYWORD_PARTITIONS_AB_EXPANDED + '": {}\n'
+              '      "' + JSON_KEYWORD_PARTITIONS_AB_EXPANDED + '": {},\n'
+              '      "' + JSON_KEYWORD_PARTITIONS_POSITION + '": {}\n'
               '    }}{}\n').format(p.label,
                                    p.offset,
                                    p.size,
@@ -481,6 +508,7 @@
                                    'true' if p.ignore else 'false',
                                    'true' if p.ab else 'false',
                                    'true' if p.ab_expanded else 'false',
+                                   p.position,
                                    '' if n == len(partitions) - 1 else ',')
     ret += ('  ]\n'
             '}\n')
@@ -724,15 +752,20 @@
         expanded_partitions.append(p)
     partitions = expanded_partitions
 
-    # Automatically generate GUIDs if the GUID is unset or set to
-    # 'auto'. Also validate the rest of the fields.
-    for p in partitions:
-      p.expand_guid(guid_generator)
-      p.validate()
-
     # Expand Disk GUID if needed.
     if not settings.disk_guid or settings.disk_guid == JSON_KEYWORD_AUTO:
-      settings.disk_guid = guid_generator.dispense_guid()
+      settings.disk_guid = guid_generator.dispense_guid(0)
+
+    # Sort according to 'position' attribute.
+    partitions = sorted(partitions, cmp=lambda x, y: x.cmp(y))
+
+    # Automatically generate GUIDs if the GUID is unset or set to
+    # 'auto'. Also validate the rest of the fields.
+    part_no = 1
+    for p in partitions:
+      p.expand_guid(guid_generator, part_no)
+      p.validate()
+      part_no += 1
 
     # Idenfify partition to grow and lay out partitions, ignoring the
     # one to grow. This way we can figure out how much space is left.
diff --git a/test/expected_json_alignment.bpt b/test/expected_json_alignment.bpt
index ba048e2..e1a13bf 100644
--- a/test/expected_json_alignment.bpt
+++ b/test/expected_json_alignment.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 1048576,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_base.bpt b/test/expected_json_base.bpt
index a1e9aa0..a496c2a 100644
--- a/test/expected_json_base.bpt
+++ b/test/expected_json_base.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_disk_guid.bpt b/test/expected_json_disk_guid.bpt
index 4a1fc80..bbbf447 100644
--- a/test/expected_json_disk_guid.bpt
+++ b/test/expected_json_disk_guid.bpt
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_size.bpt b/test/expected_json_size.bpt
index 4d6ac79..189dc9c 100644
--- a/test/expected_json_size.bpt
+++ b/test/expected_json_size.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 21474836480,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_change_ab_size.bpt b/test/expected_json_stacked_change_ab_size.bpt
index 38b7df9..c16b533 100644
--- a/test/expected_json_stacked_change_ab_size.bpt
+++ b/test/expected_json_stacked_change_ab_size.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_change_flags.bin b/test/expected_json_stacked_change_flags.bin
index 2335111..602e5c5 100644
--- a/test/expected_json_stacked_change_flags.bin
+++ b/test/expected_json_stacked_change_flags.bin
Binary files differ
diff --git a/test/expected_json_stacked_change_flags.bpt b/test/expected_json_stacked_change_flags.bpt
index 69a8ee6..12c5cdc 100644
--- a/test/expected_json_stacked_change_flags.bpt
+++ b/test/expected_json_stacked_change_flags.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0420000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_ignore.bpt b/test/expected_json_stacked_ignore.bpt
index 0a9f221..5683b07 100644
--- a/test/expected_json_stacked_ignore.bpt
+++ b/test/expected_json_stacked_ignore.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000007"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_new_partition.bpt b/test/expected_json_stacked_new_partition.bpt
index b177dd8..83636e1 100644
--- a/test/expected_json_stacked_new_partition.bpt
+++ b/test/expected_json_stacked_new_partition.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000009"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     },
     {
       "label": "my_data_partition",
@@ -100,7 +107,8 @@
       "flags": "0x8000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_new_partition_on_top.bpt b/test/expected_json_stacked_new_partition_on_top.bpt
index c2e1fa8..9f6efae 100644
--- a/test/expected_json_stacked_new_partition_on_top.bpt
+++ b/test/expected_json_stacked_new_partition_on_top.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,19 +94,21 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     },
     {
       "label": "my_partition_on_top_of_json",
       "offset": 9663655936,
       "size": 1073741824,
       "grow": false,
-      "guid": "01234567-89ab-cdef-0123-000000000007",
+      "guid": "01234567-89ab-cdef-0123-000000000008",
       "type_guid": "0fc63daf-8483-4772-8e79-3d69d8477de4",
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_override_settings.bpt b/test/expected_json_stacked_override_settings.bpt
index b52f695..2d8979c 100644
--- a/test/expected_json_stacked_override_settings.bpt
+++ b/test/expected_json_stacked_override_settings.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["-0", "-1"],
     "disk_size": 16106127360,
     "disk_alignment": 512,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot-1",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system-0",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system-1",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm-0",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm-1",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_stacked_positions.bpt b/test/expected_json_stacked_positions.bpt
new file mode 100644
index 0000000..8a173cc
--- /dev/null
+++ b/test/expected_json_stacked_positions.bpt
@@ -0,0 +1,140 @@
+{
+  "settings": {
+    "ab_suffixes": ["_a", "_b"],
+    "disk_size": 10737418240,
+    "disk_alignment": 4096,
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
+  },
+  "partitions": [
+    {
+      "label": "my_data_1",
+      "offset": 20480,
+      "size": 134217728,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000001",
+      "type_guid": "314f99d5-b2bf-4883-8d03-e2f2ce507d6a",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": false,
+      "ab_expanded": false,
+      "position": 1
+    },
+    {
+      "label": "my_data_2",
+      "offset": 134238208,
+      "size": 268435456,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000002",
+      "type_guid": "314f99d5-b2bf-4883-8d03-e2f2ce507d6a",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": false,
+      "ab_expanded": false,
+      "position": 2
+    },
+    {
+      "label": "system_a",
+      "offset": 402673664,
+      "size": 536870912,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000003",
+      "type_guid": "0f2778c4-5cc1-4300-8670-6c88b7e57ed6",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 3
+    },
+    {
+      "label": "system_b",
+      "offset": 939544576,
+      "size": 536870912,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000004",
+      "type_guid": "0f2778c4-5cc1-4300-8670-6c88b7e57ed6",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 3
+    },
+    {
+      "label": "my_data_3",
+      "offset": 1476415488,
+      "size": 536870912,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000005",
+      "type_guid": "314f99d5-b2bf-4883-8d03-e2f2ce507d6a",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": false,
+      "ab_expanded": false,
+      "position": 4
+    },
+    {
+      "label": "boot_a",
+      "offset": 2013286400,
+      "size": 33554432,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000006",
+      "type_guid": "bb499290-b57e-49f6-bf41-190386693794",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 0
+    },
+    {
+      "label": "boot_b",
+      "offset": 2046840832,
+      "size": 33554432,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000007",
+      "type_guid": "bb499290-b57e-49f6-bf41-190386693794",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 0
+    },
+    {
+      "label": "odm_a",
+      "offset": 2080395264,
+      "size": 1073741824,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000008",
+      "type_guid": "e99d84d7-2c1b-44cf-8c58-effae2dc2558",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 0
+    },
+    {
+      "label": "odm_b",
+      "offset": 3154137088,
+      "size": 1073741824,
+      "grow": false,
+      "guid": "01234567-89ab-cdef-0123-000000000009",
+      "type_guid": "e99d84d7-2c1b-44cf-8c58-effae2dc2558",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": true,
+      "ab_expanded": true,
+      "position": 0
+    },
+    {
+      "label": "userdata",
+      "offset": 4227878912,
+      "size": 6509518848,
+      "grow": true,
+      "guid": "01234567-89ab-cdef-0123-00000000000a",
+      "type_guid": "0bb7e6ed-4424-49c0-9372-7fbab465ab4c",
+      "flags": "0x0000000000000000",
+      "ignore": false,
+      "ab": false,
+      "ab_expanded": false,
+      "position": 0
+    }
+  ]
+}
diff --git a/test/expected_json_stacked_size.bpt b/test/expected_json_stacked_size.bpt
index 93fb442..06095d1 100644
--- a/test/expected_json_stacked_size.bpt
+++ b/test/expected_json_stacked_size.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["_a", "_b"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot_b",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_a",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system_b",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_a",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm_b",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/expected_json_suffixes.bpt b/test/expected_json_suffixes.bpt
index 4ceed25..8155077 100644
--- a/test/expected_json_suffixes.bpt
+++ b/test/expected_json_suffixes.bpt
@@ -3,7 +3,7 @@
     "ab_suffixes": ["-A", "-B"],
     "disk_size": 10737418240,
     "disk_alignment": 4096,
-    "disk_guid": "01234567-89ab-cdef-0123-000000000008"
+    "disk_guid": "01234567-89ab-cdef-0123-000000000000"
   },
   "partitions": [
     {
@@ -16,7 +16,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "boot-B",
@@ -28,7 +29,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system-A",
@@ -40,7 +42,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "system-B",
@@ -52,7 +55,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm-A",
@@ -64,7 +68,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "odm-B",
@@ -76,7 +81,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": true,
-      "ab_expanded": true
+      "ab_expanded": true,
+      "position": 0
     },
     {
       "label": "userdata",
@@ -88,7 +94,8 @@
       "flags": "0x0000000000000000",
       "ignore": false,
       "ab": false,
-      "ab_expanded": false
+      "ab_expanded": false,
+      "position": 0
     }
   ]
 }
diff --git a/test/positions.bpt b/test/positions.bpt
new file mode 100644
index 0000000..97b48a7
--- /dev/null
+++ b/test/positions.bpt
@@ -0,0 +1,23 @@
+{
+    "partitions": [
+        {
+            "label": "my_data_1",
+            "size": "128 MiB",
+            "position": 1
+        },
+        {
+            "label": "my_data_2",
+            "size": "256 MiB",
+            "position": 2
+        },
+        {
+            "label": "system",
+            "position": 3
+        },
+        {
+            "label": "my_data_3",
+            "size": "512 MiB",
+            "position": 4
+        }
+    ]
+}