1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# 5# Copyright 2017, Data61 6# Commonwealth Scientific and Industrial Research Organisation (CSIRO) 7# ABN 41 687 119 230. 8# 9# This software may be distributed and modified according to the terms of 10# the BSD 2-Clause license. Note that NO WARRANTY is provided. 11# See "LICENSE_BSD2.txt" for details. 12# 13# @TAG(DATA61_BSD) 14# 15 16from __future__ import absolute_import, division, print_function, \ 17 unicode_literals 18 19import inspect, os, sys, unittest 20 21ME = os.path.abspath(__file__) 22 23# Make camkes.ast importable 24sys.path.append(os.path.join(os.path.dirname(ME), '../../..')) 25 26from camkes.ast.base import * 27from camkes.ast.liftedast import * 28from camkes.ast.objects import ASTObject 29from camkes.ast.objects import * 30from camkes.internal.tests.utils import CAmkESTest 31 32class TestObjects(CAmkESTest): 33 def test_child_fields(self): 34 ''' 35 Test all the AST objects have valid `child_fields` class members. 36 37 We do this in a seemingly hacky way by dynamically discovering the AST 38 objects through the `globals` dictionary in order to ensure we notice 39 new AST object classes, even if developers forget to tell us about 40 them. 41 42 This test may seem to be checking something obvious, but it is very 43 easy to write `child_fields = ('hello')` and think you defined a tuple 44 when you really just defined a string. 45 ''' 46 for c in globals().values(): 47 if inspect.isclass(c) and issubclass(c, ASTObject): 48 self.assertTrue(hasattr(c, 'child_fields'), 49 '%s has no child_fields class member' % c.__name__) 50 self.assertIsInstance(c.child_fields, tuple, '%s\'s ' 51 'child_field member is not a tuple as expected' % 52 c.__name__) 53 seen = set() 54 for f in c.child_fields: 55 self.assertNotIn(f, seen, 56 'duplicate child field %s in %s' % (f, c.__name__)) 57 seen.add(f) 58 59 def test_no_hash(self): 60 ''' 61 Test all AST objects have a well-formed `no_hash` class member. 62 63 `ASTObject`, and all its descendents, should have a `no_hash` class 64 member that defines fields that should not be considered when doing 65 object hashes or comparisons. Importantly, children should never 66 remove `no_hash` members that their parents' defined. This test ensures 67 that every child of `ASTObject` has all the `no_hash` objects of its 68 parents. 69 ''' 70 for c in globals().values(): 71 if inspect.isclass(c) and issubclass(c, ASTObject): 72 # For each parent class between `c` and `ASTObject` inclusively 73 for parent in [p for p in c.__bases__ 74 if issubclass(p, ASTObject)]: 75 self.assertLessEqual(set(parent.no_hash), set(c.no_hash), 76 '%s discards no_hash members from its parent %s' % 77 (c.__name__, parent.__name__)) 78 79 def test_freezing_monotonic(self): 80 ''' 81 We should not be able to unfreeze (thaw) an AST object. 82 ''' 83 p = Procedure() 84 p.freeze() 85 with self.assertRaises(TypeError): 86 p.frozen = False 87 88if __name__ == '__main__': 89 unittest.main() 90