1# pylibfdt - Tests for Flat Device Tree manipulation in Python 2# Copyright (C) 2017 Google, Inc. 3# Written by Simon Glass <sjg@chromium.org> 4# 5# libfdt is dual licensed: you can use it either under the terms of 6# the GPL, or the BSD license, at your option. 7# 8# a) This library is free software; you can redistribute it and/or 9# modify it under the terms of the GNU General Public License as 10# published by the Free Software Foundation; either version 2 of the 11# License, or (at your option) any later version. 12# 13# This library is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public 19# License along with this library; if not, write to the Free 20# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21# MA 02110-1301 USA 22# 23# Alternatively, 24# 25# b) Redistribution and use in source and binary forms, with or 26# without modification, are permitted provided that the following 27# conditions are met: 28# 29# 1. Redistributions of source code must retain the above 30# copyright notice, this list of conditions and the following 31# disclaimer. 32# 2. Redistributions in binary form must reproduce the above 33# copyright notice, this list of conditions and the following 34# disclaimer in the documentation and/or other materials 35# provided with the distribution. 36# 37# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50# 51 52import sys 53import types 54import unittest 55 56sys.path.append('../pylibfdt') 57import libfdt 58from libfdt import FdtException, QUIET_NOTFOUND, QUIET_ALL 59 60def get_err(err_code): 61 """Convert an error code into an error message 62 63 Args: 64 err_code: Error code value (FDT_ERR_...) 65 66 Returns: 67 String error code 68 """ 69 return 'pylibfdt error %d: %s' % (-err_code, libfdt.fdt_strerror(-err_code)) 70 71def _ReadFdt(fname): 72 """Read a device tree file into an Fdt object, ready for use 73 74 Args: 75 fname: Filename to read from 76 77 Returns: 78 Fdt bytearray suitable for passing to libfdt functions 79 """ 80 return libfdt.Fdt(open(fname).read()) 81 82class PyLibfdtTests(unittest.TestCase): 83 """Test class for pylibfdt 84 85 Properties: 86 fdt: Device tree file used for testing 87 """ 88 89 def setUp(self): 90 """Read in the device tree we use for testing""" 91 self.fdt = _ReadFdt('test_tree1.dtb') 92 93 def GetPropList(self, node_path): 94 """Read a list of properties from a node 95 96 Args: 97 node_path: Full path to node, e.g. '/subnode@1/subsubnode' 98 99 Returns: 100 List of property names for that node, e.g. ['compatible', 'reg'] 101 """ 102 prop_list = [] 103 node = self.fdt.path_offset(node_path) 104 poffset = self.fdt.first_property_offset(node, QUIET_NOTFOUND) 105 while poffset > 0: 106 prop = self.fdt.get_property_by_offset(poffset) 107 prop_list.append(prop.name) 108 poffset = self.fdt.next_property_offset(poffset, QUIET_NOTFOUND) 109 return prop_list 110 111 def testImport(self): 112 """Check that we can import the library correctly""" 113 self.assertEquals(type(libfdt), types.ModuleType) 114 115 def testBadFdt(self): 116 """Check that a filename provided accidentally is not accepted""" 117 with self.assertRaises(FdtException) as e: 118 fdt = libfdt.Fdt('a string') 119 self.assertEquals(e.exception.err, -libfdt.BADMAGIC) 120 121 def testPathOffset(self): 122 """Check that we can find the offset of a node""" 123 self.assertEquals(self.fdt.path_offset('/'), 0) 124 self.assertTrue(self.fdt.path_offset('/subnode@1') > 0) 125 with self.assertRaises(FdtException) as e: 126 self.fdt.path_offset('/wibble') 127 self.assertEquals(e.exception.err, -libfdt.NOTFOUND) 128 self.assertEquals(self.fdt.path_offset('/wibble', QUIET_NOTFOUND), 129 -libfdt.NOTFOUND) 130 131 def testPropertyOffset(self): 132 """Walk through all the properties in the root node""" 133 offset = self.fdt.first_property_offset(0) 134 self.assertTrue(offset > 0) 135 for i in range(5): 136 next_offset = self.fdt.next_property_offset(offset) 137 self.assertTrue(next_offset > offset) 138 offset = next_offset 139 self.assertEquals(self.fdt.next_property_offset(offset, QUIET_NOTFOUND), 140 -libfdt.NOTFOUND) 141 142 def testPropertyOffsetExceptions(self): 143 """Check that exceptions are raised as expected""" 144 with self.assertRaises(FdtException) as e: 145 self.fdt.first_property_offset(107) 146 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 147 148 # Quieten the NOTFOUND exception and check that a BADOFFSET 149 # exception is still raised. 150 with self.assertRaises(FdtException) as e: 151 self.fdt.first_property_offset(107, QUIET_NOTFOUND) 152 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 153 with self.assertRaises(FdtException) as e: 154 self.fdt.next_property_offset(107, QUIET_NOTFOUND) 155 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 156 157 # Check that NOTFOUND can be quietened. 158 node = self.fdt.path_offset('/subnode@1/ss1') 159 self.assertEquals(self.fdt.first_property_offset(node, QUIET_NOTFOUND), 160 -libfdt.NOTFOUND) 161 with self.assertRaises(FdtException) as e: 162 self.fdt.first_property_offset(node) 163 self.assertEquals(e.exception.err, -libfdt.NOTFOUND) 164 165 def testGetName(self): 166 """Check that we can get the name of a node""" 167 self.assertEquals(self.fdt.get_name(0), '') 168 node = self.fdt.path_offset('/subnode@1/subsubnode') 169 self.assertEquals(self.fdt.get_name(node), 'subsubnode') 170 171 with self.assertRaises(FdtException) as e: 172 self.fdt.get_name(-2) 173 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 174 175 def testGetPropertyByOffset(self): 176 """Check that we can read the name and contents of a property""" 177 root = 0 178 poffset = self.fdt.first_property_offset(root) 179 prop = self.fdt.get_property_by_offset(poffset) 180 self.assertEquals(prop.name, 'compatible') 181 self.assertEquals(prop.value, 'test_tree1\0') 182 183 with self.assertRaises(FdtException) as e: 184 self.fdt.get_property_by_offset(-2) 185 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 186 self.assertEquals( 187 -libfdt.BADOFFSET, 188 self.fdt.get_property_by_offset(-2, [libfdt.BADOFFSET])) 189 190 def testGetProp(self): 191 """Check that we can read the contents of a property by name""" 192 root = self.fdt.path_offset('/') 193 value = self.fdt.getprop(root, "compatible") 194 self.assertEquals(value, 'test_tree1\0') 195 self.assertEquals(-libfdt.NOTFOUND, self.fdt.getprop(root, 'missing', 196 QUIET_NOTFOUND)) 197 198 with self.assertRaises(FdtException) as e: 199 self.fdt.getprop(root, 'missing') 200 self.assertEquals(e.exception.err, -libfdt.NOTFOUND) 201 202 node = self.fdt.path_offset('/subnode@1/subsubnode') 203 value = self.fdt.getprop(node, "compatible") 204 self.assertEquals(value, 'subsubnode1\0subsubnode\0') 205 206 def testStrError(self): 207 """Check that we can get an error string""" 208 self.assertEquals(libfdt.strerror(-libfdt.NOTFOUND), 209 'FDT_ERR_NOTFOUND') 210 211 def testFirstNextSubnodeOffset(self): 212 """Check that we can walk through subnodes""" 213 node_list = [] 214 node = self.fdt.first_subnode(0, QUIET_NOTFOUND) 215 while node >= 0: 216 node_list.append(self.fdt.get_name(node)) 217 node = self.fdt.next_subnode(node, QUIET_NOTFOUND) 218 self.assertEquals(node_list, ['subnode@1', 'subnode@2']) 219 220 def testFirstNextSubnodeOffsetExceptions(self): 221 """Check except handling for first/next subnode functions""" 222 node = self.fdt.path_offset('/subnode@1/subsubnode', QUIET_NOTFOUND) 223 self.assertEquals(self.fdt.first_subnode(node, QUIET_NOTFOUND), 224 -libfdt.NOTFOUND) 225 with self.assertRaises(FdtException) as e: 226 self.fdt.first_subnode(node) 227 self.assertEquals(e.exception.err, -libfdt.NOTFOUND) 228 229 node = self.fdt.path_offset('/subnode@1/ss1', QUIET_NOTFOUND) 230 self.assertEquals(self.fdt.next_subnode(node, QUIET_NOTFOUND), 231 -libfdt.NOTFOUND) 232 with self.assertRaises(FdtException) as e: 233 self.fdt.next_subnode(node) 234 self.assertEquals(e.exception.err, -libfdt.NOTFOUND) 235 236 def testDeleteProperty(self): 237 """Test that we can delete a property""" 238 node_name = '/subnode@1' 239 self.assertEquals(self.GetPropList(node_name), 240 ['compatible', 'reg', 'prop-int']) 241 node = self.fdt.path_offset('/%s' % node_name) 242 self.assertEquals(self.fdt.delprop(node, 'reg'), 0) 243 self.assertEquals(self.GetPropList(node_name), 244 ['compatible', 'prop-int']) 245 246 def testHeader(self): 247 """Test that we can access the header values""" 248 self.assertEquals(self.fdt.totalsize(), len(self.fdt._fdt)) 249 self.assertEquals(self.fdt.off_dt_struct(), 88) 250 251 def testPack(self): 252 """Test that we can pack the tree after deleting something""" 253 orig_size = self.fdt.totalsize() 254 node = self.fdt.path_offset('/subnode@2', QUIET_NOTFOUND) 255 self.assertEquals(self.fdt.delprop(node, 'prop-int'), 0) 256 self.assertEquals(orig_size, self.fdt.totalsize()) 257 self.assertEquals(self.fdt.pack(), 0) 258 self.assertTrue(self.fdt.totalsize() < orig_size) 259 260 def testBadPropertyOffset(self): 261 """Test that bad property offsets are detected""" 262 with self.assertRaises(FdtException) as e: 263 self.fdt.get_property_by_offset(13) 264 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 265 with self.assertRaises(FdtException) as e: 266 self.fdt.first_property_offset(3) 267 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 268 with self.assertRaises(FdtException) as e: 269 self.fdt.next_property_offset(3) 270 self.assertEquals(e.exception.err, -libfdt.BADOFFSET) 271 272 def testBadPathOffset(self): 273 """Test that bad path names are detected""" 274 with self.assertRaisesRegexp(FdtException, get_err(libfdt.BADPATH)): 275 self.fdt.path_offset('not-present') 276 277 def testQuietAll(self): 278 """Check that exceptions can be masked by QUIET_ALL""" 279 self.assertEquals(-libfdt.NOTFOUND, 280 self.fdt.path_offset('/missing', QUIET_ALL)) 281 self.assertEquals(-libfdt.BADOFFSET, 282 self.fdt.get_property_by_offset(13, QUIET_ALL)) 283 self.assertEquals(-libfdt.BADPATH, 284 self.fdt.path_offset('missing', QUIET_ALL)) 285 286 287if __name__ == "__main__": 288 unittest.main() 289