Add 'prioritize' command to cgpt tool.

This lets us reorder the priority of all the kernel partitions with a single
command, instead of a bunch of complicated and error-prone shell script
logic.

Change-Id: I21d39763ec5a748488d5319a987bcfe7c34ce4d0

BUG=chromium-os:9167
TEST=manual

In the chroot, do this:

cd ~/trunk/src/platform/vboot_reference
make
make runtests
make clean

Everything should pass.

Review URL: http://codereview.chromium.org/5352005
diff --git a/tests/run_cgpt_tests.sh b/tests/run_cgpt_tests.sh
index 577d7b1..ffd6f68 100755
--- a/tests/run_cgpt_tests.sh
+++ b/tests/run_cgpt_tests.sh
@@ -81,27 +81,27 @@
 
 X=$($GPT show -b -i $DATA_NUM ${DEV})
 Y=$($GPT show -s -i $DATA_NUM ${DEV})
-[ "$X $Y" = "$DATA_START $DATA_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$DATA_START $DATA_SIZE" ] || error
 
 X=$($GPT show -b -i $KERN_NUM ${DEV})
 Y=$($GPT show -s -i $KERN_NUM ${DEV})
-[ "$X $Y" = "$KERN_START $KERN_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$KERN_START $KERN_SIZE" ] || error
 
 X=$($GPT show -b -i $ROOTFS_NUM ${DEV})
 Y=$($GPT show -s -i $ROOTFS_NUM ${DEV})
-[ "$X $Y" = "$ROOTFS_START $ROOTFS_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$ROOTFS_START $ROOTFS_SIZE" ] || error
 
 X=$($GPT show -b -i $ESP_NUM ${DEV})
 Y=$($GPT show -s -i $ESP_NUM ${DEV})
-[ "$X $Y" = "$ESP_START $ESP_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$ESP_START $ESP_SIZE" ] || error
 
 X=$($GPT show -b -i $FUTURE_NUM ${DEV})
 Y=$($GPT show -s -i $FUTURE_NUM ${DEV})
-[ "$X $Y" = "$FUTURE_START $FUTURE_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$FUTURE_START $FUTURE_SIZE" ] || error
 
 X=$($GPT show -b -i $RANDOM_NUM ${DEV})
 Y=$($GPT show -s -i $RANDOM_NUM ${DEV})
-[ "$X $Y" = "$RANDOM_START $RANDOM_SIZE" ] || error "fail at line $LINENO"
+[ "$X $Y" = "$RANDOM_START $RANDOM_SIZE" ] || error
 
 
 echo "Set the boot partition.."
@@ -110,7 +110,127 @@
 echo "Check the PMBR's idea of the boot partition..."
 X=$($GPT boot ${DEV})
 Y=$($GPT show -u -i $KERN_NUM $DEV)
-[ "$X" = "$Y" ] || error "fail at line $LINENO"
+[ "$X" = "$Y" ] || error
+
+
+echo "Test the cgpt prioritize command..."
+
+# Input: sequence of priorities
+# Output: ${DEV} has kernel partitions with the given priorities
+make_pri() {
+  local idx=0
+  $GPT create ${DEV}
+  for pri in "$@"; do
+    idx=$((idx+1))
+    $GPT add -t kernel -l "kern$idx" -b $((100 + 2 * $idx)) -s 1 -P $pri ${DEV}
+  done
+}
+
+# Output: returns string containing priorities of all kernels
+get_pri() {
+  echo $(
+  for idx in $($GPT find -t kernel ${DEV} | sed -e s@${DEV}@@); do
+    $GPT show -i $idx -P ${DEV}
+  done
+  )
+}
+
+# Input: list of priorities
+# Operation: expects ${DEV} to contain those kernel priorities
+assert_pri() {
+  local expected="$*"
+  local actual=$(get_pri)
+  [ "$actual" = "$expected" ] || \
+    error 1 "expected priority \"$expected\", actual priority \"$actual\""
+}
+
+
+# no kernels at all. This should do nothing.
+$GPT create ${DEV}
+$GPT add -t rootfs -b 100 -s 1 ${DEV}
+$GPT prioritize ${DEV}
+assert_pri ""
+
+# common install/upgrade sequence
+make_pri   2 0 0
+$GPT prioritize -i 1 ${DEV}
+assert_pri 1 0 0
+$GPT prioritize -i 2 ${DEV}
+assert_pri 1 2 0
+$GPT prioritize -i 1 ${DEV}
+assert_pri 2 1 0
+$GPT prioritize -i 2 ${DEV}
+assert_pri 1 2 0
+
+# lots of kernels, all same starting priority, should go to priority 1
+make_pri   8 8 8 8 8 8 8 8 8 8 8 0 0 8
+$GPT prioritize ${DEV}
+assert_pri 1 1 1 1 1 1 1 1 1 1 1 0 0 1
+
+# now raise them all up again
+$GPT prioritize -P 4 ${DEV}
+assert_pri 4 4 4 4 4 4 4 4 4 4 4 0 0 4
+
+# set one of them higher, should leave the rest alone
+$GPT prioritize -P 5 -i 3 ${DEV}
+assert_pri 4 4 5 4 4 4 4 4 4 4 4 0 0 4
+
+# set one of them lower, should bring the rest down
+$GPT prioritize -P 3 -i 4 ${DEV}
+assert_pri 1 1 2 3 1 1 1 1 1 1 1 0 0 1
+
+# raise a group by including the friends of one partition
+$GPT prioritize -P 6 -i 1 -f ${DEV}
+assert_pri 6 6 4 5 6 6 6 6 6 6 6 0 0 6
+
+# resurrect one, should not affect the others
+make_pri   0 0 0 0 0 0 0 0 0 0 0 0 0 0
+$GPT prioritize -i 2 ${DEV}
+assert_pri 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+
+# resurrect one and all its friends
+make_pri   0 0 0 0 0 0 0 0 1 2 0 0 0 0
+$GPT prioritize -P 5 -i 2 -f ${DEV}
+assert_pri 5 5 5 5 5 5 5 5 3 4 5 5 5 5
+
+# no options should maintain the same order
+$GPT prioritize ${DEV}
+assert_pri 3 3 3 3 3 3 3 3 1 2 3 3 3 3
+
+# squish all the ranks
+make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
+$GPT prioritize -P 6 ${DEV}
+assert_pri 1 1 1 1 2 2 3 3 4 4 0 5 6 6
+
+# squish the ranks by not leaving room
+make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
+$GPT prioritize -P 7 -i 3 ${DEV}
+assert_pri 1 1 7 1 2 2 3 3 4 4 0 5 6 6
+
+# squish the ranks while bringing the friends along
+make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
+$GPT prioritize -P 6 -i 3 -f ${DEV}
+assert_pri 1 1 6 6 1 1 2 2 3 3 0 4 5 5
+
+# squish them pretty hard
+make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
+$GPT prioritize -P 2 ${DEV}
+assert_pri 1 1 1 1 1 1 1 1 1 1 0 1 2 2
+
+# squish them really really hard (nobody gets reduced to zero, though)
+make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
+$GPT prioritize -P 1 -i 3 ${DEV}
+assert_pri 1 1 1 1 1 1 1 1 1 1 0 1 1 1
+
+# squish if we try to go too high
+make_pri   15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0
+$GPT prioritize -i 3 ${DEV}
+assert_pri 14 14 15 13 12 12 11 11 10 10  9  9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 0
+$GPT prioritize -i 5 ${DEV}
+assert_pri 13 13 14 12 15 11 10 10  9  9  8  8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 1 1 0
+# but if I bring friends I don't have to squish
+$GPT prioritize -i 1 -f ${DEV}
+assert_pri 15 15 13 12 14 11 10 10  9  9  8  8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 1 1 0
 
 echo "Done."