1# Copyright (C) 2016 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import adb 16import argparse 17import os 18import unittest 19import fastboot 20import subprocess 21import sys 22 23# Default values for arguments 24device_type = "phone" 25 26class ShellTest(unittest.TestCase): 27 @classmethod 28 def setUpClass(cls): 29 cls.fastboot = fastboot.FastbootDevice() 30 31 def exists_validvals(self, varname, varlist, validlist): 32 self.assertIn(varname, varlist) 33 self.assertIn(varlist[varname], validlist) 34 return varlist[varname] 35 36 def exists_yes_no(self, varname, varlist): 37 return self.exists_validvals(varname, varlist, ["yes", "no"]) 38 39 def exists_nonempty(self, varname, varlist): 40 self.assertIn(varname, varlist) 41 self.assertGreater(len(varlist[varname]), 0) 42 return varlist[varname] 43 44 def exists_integer(self, varname, varlist, base=10): 45 val = 0 46 self.assertIn(varname, varlist) 47 try: 48 val = int(varlist[varname], base) 49 except ValueError: 50 self.fail("%s (%s) is not an integer" % (varname, varlist[varname])) 51 return val 52 53 def get_exists(self, varname): 54 val = self.fastboot.getvar(varname) 55 self.assertIsNotNone(val) 56 return val 57 58 def get_exists_validvals(self, varname, validlist): 59 val = self.get_exists(varname) 60 self.assertIn(val, validlist) 61 return val 62 63 def get_exists_yes_no(self, varname): 64 return self.get_exists_validvals(varname, ["yes", "no"]) 65 66 def get_exists_nonempty(self, varname): 67 val = self.get_exists(varname) 68 self.assertGreater(len(val), 0) 69 return val 70 71 def get_exists_integer(self, varname, base=10): 72 val = self.get_exists(varname) 73 try: 74 num = int(val, base) 75 except ValueError: 76 self.fail("%s (%s) is not an integer" % (varname, val)) 77 return num 78 79 def get_slotcount(self): 80 slotcount = 0 81 try: 82 val = self.fastboot.getvar("slot-count") 83 if val != None: 84 slotcount = int(val) 85 except ValueError: 86 self.fail("slot-count (%s) is not an integer" % val) 87 except subprocess.CalledProcessError: 88 print "Does not appear to be an A/B device." 89 if not slotcount: 90 print "Does not appear to be an A/B device." 91 return slotcount 92 93 def test_getvarall(self): 94 """Tests that required variables are reported by getvar all""" 95 96 var_all = self.fastboot.getvar_all() 97 self.exists_nonempty("version-baseband", var_all) 98 self.exists_nonempty("version-bootloader", var_all) 99 self.exists_nonempty("product", var_all) 100 self.exists_yes_no("secure", var_all) 101 self.exists_yes_no("unlocked", var_all) 102 self.exists_validvals("off-mode-charge", var_all, ["0", "1"]) 103 self.assertIn("variant", var_all) 104 voltage = self.exists_nonempty("battery-voltage", var_all) 105 if voltage[-2:].lower() == "mv": 106 voltage = voltage[:-2] 107 try: 108 voltnum = float(voltage) 109 except ValueError: 110 self.fail("battery-voltage (%s) is not a number" % (varname, voltage)) 111 self.exists_yes_no("battery-soc-ok", var_all) 112 maxdl = self.exists_integer("max-download-size", var_all, 16) 113 self.assertGreater(maxdl, 0) 114 115 if "slot-count" in var_all: 116 try: 117 slotcount = int(var_all["slot-count"]) 118 except ValueError: 119 self.fail("slot-count (%s) is not an integer" % var_all["slot-count"]) 120 if slotcount > 1: 121 # test for A/B variables 122 slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] 123 self.exists_validvals("current-slot", var_all, slots) 124 125 # test for slot metadata 126 for slot in slots: 127 self.exists_yes_no("slot-unbootable:"+slot, var_all) 128 self.exists_yes_no("slot-unbootable:"+slot, var_all) 129 self.exists_integer("slot-retry-count:"+slot, var_all) 130 else: 131 print "This does not appear to be an A/B device." 132 133 def test_getvar_nonexistent(self): 134 """Tests behaviour of nonexistent variables.""" 135 136 self.assertIsNone(self.fastboot.getvar("fhqwhgads")) 137 138 def test_getvar(self): 139 """Tests all variables separately""" 140 141 self.get_exists_nonempty("version-baseband") 142 self.get_exists_nonempty("version-bootloader") 143 self.get_exists_nonempty("product") 144 self.get_exists_yes_no("secure") 145 self.get_exists_yes_no("unlocked") 146 self.get_exists_validvals("off-mode-charge", ["0", "1"]) 147 self.get_exists("variant") 148 voltage = self.get_exists_nonempty("battery-voltage") 149 if voltage[-2:].lower() == "mv": 150 voltage = voltage[:-2] 151 try: 152 voltnum = float(voltage) 153 except ValueError: 154 self.fail("battery-voltage (%s) is not a number" % voltage) 155 self.get_exists_yes_no("battery-soc-ok") 156 maxdl = self.get_exists_integer("max-download-size", 16) 157 self.assertGreater(maxdl, 0) 158 159 slotcount = self.get_slotcount() 160 if slotcount > 1: 161 # test for A/B variables 162 slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] 163 self.get_exists_validvals("current-slot", slots) 164 165 # test for slot metadata 166 for slot in slots: 167 self.get_exists_yes_no("slot-unbootable:"+slot) 168 self.get_exists_yes_no("slot-successful:"+slot) 169 self.get_exists_integer("slot-retry-count:"+slot) 170 171 def test_setactive(self): 172 """Tests that A/B devices can switch to each slot, and the change persists over a reboot.""" 173 # Test invalid if not an A/B device 174 slotcount = self.get_slotcount() 175 if not slotcount: 176 return 177 178 maxtries = 0 179 slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)] 180 for slot in slots: 181 self.fastboot.set_active(slot) 182 self.assertEqual(slot, self.fastboot.getvar("current-slot")) 183 self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot)) 184 self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot)) 185 retry = self.get_exists_integer("slot-retry-count:"+slot) 186 if maxtries == 0: 187 maxtries = retry 188 else: 189 self.assertEqual(maxtries, retry) 190 self.fastboot.reboot(True) 191 self.assertEqual(slot, self.fastboot.getvar("current-slot")) 192 self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot)) 193 self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot)) 194 retry = self.get_exists_integer("slot-retry-count:"+slot) 195 if maxtries == 0: 196 maxtries = retry 197 else: 198 self.assertEqual(maxtries, retry) 199 200 def test_hasslot(self): 201 """Tests that A/B devices report partitions that have slots.""" 202 # Test invalid if not an A/B device 203 if not self.get_slotcount(): 204 return 205 206 self.assertEqual("yes", self.fastboot.getvar("has-slot:system")) 207 self.assertEqual("yes", self.fastboot.getvar("has-slot:boot")) 208 209 # Additional partition on AndroidThings (IoT) devices 210 if device_type == "iot": 211 self.assertEqual("yes", self.fastboot.getvar("has-slot:oem")) 212 213if __name__ == '__main__': 214 parser = argparse.ArgumentParser() 215 parser.add_argument("--device-type", default="phone", 216 help="Type of device ('phone' or 'iot').") 217 parser.add_argument("extra_args", nargs="*") 218 args = parser.parse_args() 219 220 if args.device_type.lower() not in ("phone", "iot"): 221 raise ValueError("Unsupported device type '%s'." % args.device_type) 222 device_type = args.device_type.lower() 223 224 sys.argv[1:] = args.extra_args 225 unittest.main(verbosity=3) 226