1#!/usr/bin/python3 2# 3# Copyright 2019 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 7import json 8import os 9import tempfile 10import unittest 11 12import common 13 14from autotest_lib.client.common_lib import error 15from autotest_lib.server.cros.faft.utils import config 16 17 18class CanLoadDefaultTestCase(unittest.TestCase): 19 """Ensure that configs can load the default JSON""" 20 21 def runTest(self): 22 """Main test logic""" 23 platform = "foo" 24 cfg = config.Config(platform) 25 self.assertIsInstance(cfg.has_keyboard, bool) 26 27 28class _MockConfigTestCaseBaseClass(unittest.TestCase): 29 """ 30 Base class which handles the setup/teardown of mock config files. 31 32 Sub-classes should declare a class attribute, mock_configs, 33 as a dict representing all platforms to be written as JSON files. 34 This class writes those JSON files during setUp() and deletes them 35 during tearDown(). 36 During runTest(), sub-classes can create config.Config instances by name 37 and run assertions as normal. 38 39 """ 40 41 mock_configs = None 42 43 def setUp(self): 44 """Set up a tempfile containing the test data""" 45 if self.mock_configs is None: 46 return 47 48 # Setup mock config._CONFIG_DIR, but remember the original. 49 self.mock_config_dir = tempfile.mkdtemp() 50 self.original_config_dir = config._CONFIG_DIR 51 config._CONFIG_DIR = self.mock_config_dir 52 53 # Write mock config file. 54 with open(config._consolidated_json_fp(), 'w') as f: 55 json.dump(self.mock_configs, f) 56 57 def tearDown(self): 58 """After tests are complete, delete the tempfile""" 59 if self.mock_configs is None: 60 return 61 os.remove(config._consolidated_json_fp()) 62 os.rmdir(self.mock_config_dir) 63 config._CONFIG_DIR = self.original_config_dir 64 65 66class InheritanceTestCase(_MockConfigTestCaseBaseClass): 67 """Ensure that platforms inherit attributes correctly""" 68 69 mock_configs = { 70 'DEFAULTS': { 71 'no_override': 'default', 72 'parent_override': 'default', 73 'child_override': 'default', 74 'both_override': 'default', 75 'parent': None 76 }, 77 'childboard': { 78 'child_override': 'child', 79 'both_override': 'child', 80 'parent': 'parentboard' 81 }, 82 'parentboard': { 83 'parent_override': 'parent', 84 'both_override': 'parent' 85 } 86 } 87 88 def runTest(self): 89 """ 90 Verify that the following situations resolve correctly: 91 A platform that inherit some overridess from another platform 92 A platform that does not inherit from another platform 93 A platform not found in the config file 94 """ 95 child_config = config.Config('childboard') 96 #print(child_config) 97 self.assertEqual(child_config.no_override, 'default') 98 self.assertEqual(child_config.parent_override, 'parent') 99 self.assertEqual(child_config.child_override, 'child') 100 self.assertEqual(child_config.both_override, 'child') 101 with self.assertRaises(AttributeError): 102 child_config.foo # pylint: disable=pointless-statement 103 104 parent_config = config.Config('parentboard') 105 self.assertEqual(parent_config.no_override, 'default') 106 self.assertEqual(parent_config.parent_override, 'parent') 107 self.assertEqual(parent_config.child_override, 'default') 108 self.assertEqual(parent_config.both_override, 'parent') 109 110 foo_config = config.Config('foo') 111 self.assertEqual(foo_config.no_override, 'default') 112 self.assertEqual(foo_config.parent_override, 'default') 113 self.assertEqual(foo_config.child_override, 'default') 114 self.assertEqual(foo_config.both_override, 'default') 115 116 # While we're here, verify that str(config) doesn't break 117 str(child_config) # pylint: disable=pointless-statement 118 119 120class ModelOverrideTestCase(_MockConfigTestCaseBaseClass): 121 """Verify that models of boards inherit overrides with proper precedence""" 122 mock_configs = { 123 'parentboard': { 124 'attr1': 'parent_attr1', 125 'attr2': 'parent_attr2', 126 'models': { 127 'modelA': { 128 'attr1': 'parent_modelA_attr1' 129 } 130 } 131 }, 132 'childboard': { 133 'parent': 'parentboard', 134 'attr1': 'child_attr1', 135 'models': { 136 'modelA': { 137 'attr1': 'child_modelA_attr1' 138 } 139 } 140 }, 141 'DEFAULTS': { 142 'models': None, 143 'attr1': 'default', 144 'attr2': 'default' 145 } 146 } 147 148 def runTest(self): 149 """Run assertions on test data""" 150 child_config = config.Config('childboard') 151 child_modelA_config = config.Config('childboard', 'modelA') 152 child_modelB_config = config.Config('childboard', 'modelB') 153 parent_config = config.Config('parentboard') 154 parent_modelA_config = config.Config('parentboard', 'modelA') 155 parent_modelB_config = config.Config('parentboard', 'modelB') 156 157 self.assertEqual(child_config.attr1, 'child_attr1') 158 self.assertEqual(child_config.attr2, 'parent_attr2') 159 self.assertEqual(child_modelA_config.attr1, 'child_modelA_attr1') 160 self.assertEqual(child_modelA_config.attr2, 'parent_attr2') 161 self.assertEqual(child_modelB_config.attr1, 'child_attr1') 162 self.assertEqual(child_modelB_config.attr2, 'parent_attr2') 163 self.assertEqual(parent_config.attr1, 'parent_attr1') 164 self.assertEqual(parent_config.attr2, 'parent_attr2') 165 self.assertEqual(parent_modelA_config.attr1, 'parent_modelA_attr1') 166 self.assertEqual(parent_modelA_config.attr2, 'parent_attr2') 167 self.assertEqual(parent_modelB_config.attr1, 'parent_attr1') 168 self.assertEqual(parent_modelB_config.attr2, 'parent_attr2') 169 170 171class DirectSelfInheritanceTestCase(_MockConfigTestCaseBaseClass): 172 """Ensure that a config which inherits from itself raises an error.""" 173 174 mock_configs = { 175 'selfloop': { 176 'parent': 'selfloop', 177 }, 178 } 179 180 def runTest(self): 181 """Run assertions on test data.""" 182 with self.assertRaises(error.TestError): 183 config.Config('selfloop') 184 185 186class IndirectSelfInheritanceTestCase(_MockConfigTestCaseBaseClass): 187 """Ensure that configs which inherit from each other raise an error.""" 188 189 mock_configs = { 190 'indirectloop1': { 191 'parent': 'indirectloop2', 192 }, 193 'indirectloop2': { 194 'parent': 'indirectloop1', 195 }, 196 'indirectloop3': { 197 'parent': 'indirectloop1', 198 }, 199 } 200 201 def runTest(self): 202 """Run assertions on test data.""" 203 with self.assertRaises(error.TestError): 204 config.Config('indirectloop1') 205 with self.assertRaises(error.TestError): 206 config.Config('indirectloop3') 207 208 209class FindMostSpecificConfigTestCase(_MockConfigTestCaseBaseClass): 210 """Ensure that configs named like $BOARD-kernelnext load $BOARD.json.""" 211 212 mock_configs = { 213 'DEFAULTS': {}, 214 'samus': {}, 215 'veyron': {}, 216 'minnie': {'parent': 'veyron'}, 217 } 218 219 def runTest(self): 220 cfg = config.Config('samus-kernelnext') 221 self.assertEqual(config.Config('samus-kernelnext').platform, 'samus') 222 self.assertEqual(config.Config('samus-arc-r').platform, 'samus') 223 self.assertEqual(config.Config('veyron_minnie').platform, 'minnie') 224 self.assertEqual(config.Config('veyron_monroe').platform, 'veyron') 225 self.assertEqual(config.Config('veyron_minnie-arc-r').platform, 'minnie') 226 self.assertEqual(config.Config('veyron_monroe-arc-r').platform, 'veyron') 227 228 229if __name__ == '__main__': 230 unittest.main() 231