1- import asyncio
2- import io
3- import sys
4-
5- from microcotb .time .value import TimeValue
6- from ttboard .demoboard import DemoBoard
7-
8- def start_soon (c ):
9- pass
10-
11- class TestCase :
12- def __init__ (self ,
13- name :str ,
14- func ,
15- timeout_time : float = None ,
16- timeout_unit : str = '' ,
17- expect_fail : bool = False ,
18- expect_error :Exception = None ,
19- skip : bool = False ,
20- stage : int = 0 ):
21- self .name = name
22- self .function = func
23- self .timeout = None
24- if timeout_time is not None :
25- if not len (timeout_unit ):
26- raise ValueError ('Must specify a timeout_unit when using timeouts' )
27- self .timeout = TimeValue (timeout_time , timeout_unit )
28-
29- self .expect_fail = expect_fail
30- self .expect_error = expect_error
31- self .skip = skip
32- self .stage = stage
33- self .failed = False
34- self .failed_msg = ''
35-
36- def run (self , dut ):
37- if self .skip :
38- dut ._log .warn (f"{ self .name } skip=True" )
39- return
40- func = self .function
41- try :
42- asyncio .run (func (dut ))
43- except Exception as e :
44- self .failed = True
45- if not self .expect_fail :
46- raise e
47-
48- buf = io .StringIO ()
49- sys .print_exception (e , buf )
50- dut ._log .error (buf .getvalue ())
51- dut ._log .warn ("Failure was expected" )
52-
53- _RunnerSingleton = None
54- class Runner :
55-
56- @classmethod
57- def get (cls ):
58- global _RunnerSingleton
59- if _RunnerSingleton is None :
60- _RunnerSingleton = cls ()
61-
62- return _RunnerSingleton
63-
64- @classmethod
65- def clear_all (cls ):
66- global _RunnerSingleton
67- # clear the singleton
68- _RunnerSingleton = None
69-
70- def __init__ (self ):
71- self .tests_to_run = dict ()
72- self .test_names = []
73-
74- def add_test (self , test :TestCase ):
75- if test .name is None :
76- test .name = f'test_{ test .function .__name__ } '
77- self .test_names .append (test .name )
78- self .tests_to_run [test .name ] = test
79-
80-
81- def test (self , dut ):
82- from microcotb .time .system import SystemTime
83- from microcotb .clock import Clock
84-
85- ttdb = DemoBoard .get ()
86- if ttdb .is_auto_clocking :
87- dut ._log .debug ("Stopping ttdb auto-clocking" )
88- ttdb .clock_project_stop ()
89-
90- num_failures = 0
91- num_tests = len (self .test_names )
92- #failures = dict()
93- for test_count in range (num_tests ):
94- nm = self .test_names [test_count ]
95- SystemTime .reset ()
96- Clock .clear_all ()
97- test = self .tests_to_run [nm ]
98-
99- if test .timeout is None :
100- SystemTime .clear_timeout ()
101- else :
102- SystemTime .set_timeout (test .timeout )
103-
104-
105- test .failed = False
106- try :
107- dut ._log .info (f"*** Running Test { test_count + 1 } /{ num_tests } : { nm } ***" )
108- test .run (dut )
109- if test .expect_fail :
110- num_failures += 1
111- dut ._log .error (f"*** { nm } expected fail, so PASS ***" )
112- else :
113- dut ._log .warn (f"*** Test '{ nm } ' PASS ***" )
114- except Exception as e :
115-
116- test .failed = True
117- buf = io .StringIO ()
118- sys .print_exception (e , buf )
119- dut ._log .error (buf .getvalue ())
120- if len (e .args ):
121- dut ._log .error (f"T*** Test '{ nm } ' FAIL: { e .args [0 ]} ***" )
122- if e .args [0 ] is None or not e .args [0 ]:
123- test .failed_msg = ''
124- else :
125- test .failed_msg = e .args [0 ]
126-
127- num_failures += 1
128-
129-
130- if num_failures :
131- dut ._log .warn (f"{ num_failures } /{ len (self .test_names )} tests failed" )
132- else :
133- dut ._log .info (f"All { len (self .test_names )} tests passed" )
134-
135- dut ._log .info ("*** Summary ***" )
136- for nm in self .test_names :
137- test = self .tests_to_run [nm ]
138- if test .failed :
139- if test .expect_fail :
140- dut ._log .warn (f"\t PASS\t { nm } \t Failed as expected { test .failed_msg } " )
141- else :
142- dut ._log .error (f"\t FAIL\t { nm } \t { test .failed_msg } " )
143- else :
144- if self .tests_to_run [nm ].skip :
145- dut ._log .warn (f"\t SKIP\t { nm } " )
146- else :
147- if test .expect_fail :
148- dut ._log .error (f"\t FAIL\t { nm } \t passed but expect_fail = True" )
149- else :
150- dut ._log .warn (f"\t PASS\t { nm } " )
151-
152-
153-
154- def get_runner (sim = None ):
155- return Runner .get ()
156-
157-
158-
159-
160- def test (func = None , * ,
161- timeout_time : float = None ,
162- timeout_unit : str = "step" ,
163- expect_fail : bool = False ,
164- expect_error :Exception = None ,
165- skip : bool = False ,
166- stage : int = 0 ,
167- name : str = None ):
168-
169- def my_decorator_func (func ):
170- runner = Runner .get ()
171- test_name = func .__name__ if name is None else name
172- test_case = TestCase (test_name , func ,
173- timeout_time ,
174- timeout_unit ,
175- expect_fail ,
176- expect_error ,
177- skip ,
178- stage )
179-
180- def wrapper_func (dut ):
181- test_case .run (dut )
182-
183- runner .add_test (test_case )
184- return wrapper_func
185-
186- return my_decorator_func
1+ from microcotb import *
0 commit comments