| # Copyright (C) 2016 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from __future__ import print_function |
| |
| import argparse |
| import os |
| import unittest |
| import fastboot |
| import subprocess |
| import sys |
| |
| # Default values for arguments |
| device_type = "phone" |
| |
| class ShellTest(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls): |
| cls.fastboot = fastboot.FastbootDevice() |
| |
| def exists_validvals(self, varname, varlist, validlist): |
| self.assertIn(varname, varlist) |
| self.assertIn(varlist[varname], validlist) |
| return varlist[varname] |
| |
| def exists_yes_no(self, varname, varlist): |
| return self.exists_validvals(varname, varlist, ["yes", "no"]) |
| |
| def exists_nonempty(self, varname, varlist): |
| self.assertIn(varname, varlist) |
| self.assertGreater(len(varlist[varname]), 0) |
| return varlist[varname] |
| |
| def exists_integer(self, varname, varlist, base=10): |
| val = 0 |
| self.assertIn(varname, varlist) |
| try: |
| val = int(varlist[varname], base) |
| except ValueError: |
| self.fail("%s (%s) is not an integer" % (varname, varlist[varname])) |
| return val |
| |
| def get_exists(self, varname): |
| val = self.fastboot.getvar(varname) |
| self.assertIsNotNone(val) |
| return val |
| |
| def get_exists_validvals(self, varname, validlist): |
| val = self.get_exists(varname) |
| self.assertIn(val, validlist) |
| return val |
| |
| def get_exists_yes_no(self, varname): |
| return self.get_exists_validvals(varname, ["yes", "no"]) |
| |
| def get_exists_nonempty(self, varname): |
| val = self.get_exists(varname) |
| self.assertGreater(len(val), 0) |
| return val |
| |
| def get_exists_integer(self, varname, base=10): |
| val = self.get_exists(varname) |
| try: |
| num = int(val, base) |
| except ValueError: |
| self.fail("%s (%s) is not an integer" % (varname, val)) |
| return num |
| |
| def get_slotcount(self): |
| slotcount = 0 |
| try: |
| val = self.fastboot.getvar("slot-count") |
| if val != None: |
| slotcount = int(val) |
| except ValueError: |
| self.fail("slot-count (%s) is not an integer" % val) |
| except subprocess.CalledProcessError: |
| print("Does not appear to be an A/B device.") |
| if not slotcount: |
| print("Does not appear to be an A/B device.") |
| return slotcount |
| |
| def test_getvarall(self): |
| """Tests that required variables are reported by getvar all""" |
| |
| var_all = self.fastboot.getvar_all() |
| self.exists_nonempty("version-baseband", var_all) |
| self.exists_nonempty("version-bootloader", var_all) |
| self.exists_nonempty("product", var_all) |
| self.exists_yes_no("secure", var_all) |
| self.exists_yes_no("unlocked", var_all) |
| self.exists_validvals("off-mode-charge", var_all, ["0", "1"]) |
| self.assertIn("variant", var_all) |
| voltage = self.exists_nonempty("battery-voltage", var_all) |
| if voltage[-2:].lower() == "mv": |
| voltage = voltage[:-2] |
| try: |
| voltnum = float(voltage) |
| except ValueError: |
| self.fail("battery-voltage (%s) is not a number" % (varname, voltage)) |
| self.exists_yes_no("battery-soc-ok", var_all) |
| maxdl = self.exists_integer("max-download-size", var_all, 16) |
| self.assertGreater(maxdl, 0) |
| |
| if "slot-count" in var_all: |
| try: |
| slotcount = int(var_all["slot-count"]) |
| except ValueError: |
| self.fail("slot-count (%s) is not an integer" % var_all["slot-count"]) |
| if slotcount > 1: |
| # test for A/B variables |
| slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] |
| self.exists_validvals("current-slot", var_all, slots) |
| |
| # test for slot metadata |
| for slot in slots: |
| self.exists_yes_no("slot-unbootable:"+slot, var_all) |
| self.exists_yes_no("slot-unbootable:"+slot, var_all) |
| self.exists_integer("slot-retry-count:"+slot, var_all) |
| else: |
| print("This does not appear to be an A/B device.") |
| |
| def test_getvar_nonexistent(self): |
| """Tests behaviour of nonexistent variables.""" |
| |
| self.assertIsNone(self.fastboot.getvar("fhqwhgads")) |
| |
| def test_getvar(self): |
| """Tests all variables separately""" |
| |
| self.get_exists_nonempty("version-baseband") |
| self.get_exists_nonempty("version-bootloader") |
| self.get_exists_nonempty("product") |
| self.get_exists_yes_no("secure") |
| self.get_exists_yes_no("unlocked") |
| self.get_exists_validvals("off-mode-charge", ["0", "1"]) |
| self.get_exists("variant") |
| voltage = self.get_exists_nonempty("battery-voltage") |
| if voltage[-2:].lower() == "mv": |
| voltage = voltage[:-2] |
| try: |
| voltnum = float(voltage) |
| except ValueError: |
| self.fail("battery-voltage (%s) is not a number" % voltage) |
| self.get_exists_yes_no("battery-soc-ok") |
| maxdl = self.get_exists_integer("max-download-size", 16) |
| self.assertGreater(maxdl, 0) |
| |
| slotcount = self.get_slotcount() |
| if slotcount > 1: |
| # test for A/B variables |
| slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] |
| self.get_exists_validvals("current-slot", slots) |
| |
| # test for slot metadata |
| for slot in slots: |
| self.get_exists_yes_no("slot-unbootable:"+slot) |
| self.get_exists_yes_no("slot-successful:"+slot) |
| self.get_exists_integer("slot-retry-count:"+slot) |
| |
| def test_setactive(self): |
| """Tests that A/B devices can switch to each slot, and the change persists over a reboot.""" |
| # Test invalid if not an A/B device |
| slotcount = self.get_slotcount() |
| if not slotcount: |
| return |
| |
| maxtries = 0 |
| slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] |
| for slot in slots: |
| self.fastboot.set_active(slot) |
| self.assertEqual(slot, self.fastboot.getvar("current-slot")) |
| self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot)) |
| self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot)) |
| retry = self.get_exists_integer("slot-retry-count:"+slot) |
| if maxtries == 0: |
| maxtries = retry |
| else: |
| self.assertEqual(maxtries, retry) |
| self.fastboot.reboot(True) |
| self.assertEqual(slot, self.fastboot.getvar("current-slot")) |
| self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot)) |
| self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot)) |
| retry = self.get_exists_integer("slot-retry-count:"+slot) |
| if maxtries == 0: |
| maxtries = retry |
| else: |
| self.assertEqual(maxtries, retry) |
| |
| def test_hasslot(self): |
| """Tests that A/B devices report partitions that have slots.""" |
| # Test invalid if not an A/B device |
| if not self.get_slotcount(): |
| return |
| |
| self.assertEqual("yes", self.fastboot.getvar("has-slot:system")) |
| self.assertEqual("yes", self.fastboot.getvar("has-slot:boot")) |
| |
| # Additional partition on AndroidThings (IoT) devices |
| if device_type == "iot": |
| self.assertEqual("yes", self.fastboot.getvar("has-slot:oem")) |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--device-type", default="phone", |
| help="Type of device ('phone' or 'iot').") |
| parser.add_argument("extra_args", nargs="*") |
| args = parser.parse_args() |
| |
| if args.device_type.lower() not in ("phone", "iot"): |
| raise ValueError("Unsupported device type '%s'." % args.device_type) |
| device_type = args.device_type.lower() |
| |
| sys.argv[1:] = args.extra_args |
| unittest.main(verbosity=3) |