1414
1515# pylint: disable=missing-function-docstring
1616
17- import argparse
18- import contextlib
1917import io
2018import os
19+ import shlex
20+ import subprocess
2121import sys
22- import replace_all
22+
23+
24+ _out = io .StringIO ()
25+
26+
27+ class ValidationTest :
28+ """Base class for validation tests."""
29+
30+ def setup (self ):
31+ # Clear the buffer
32+ _out .seek (0 )
33+ _out .truncate (0 )
34+
35+ def teardown (self ):
36+ pass
37+
38+ def run_test (self , name , func ):
39+ print (f"{ name } : " , end = "" , flush = True )
40+ self .setup ()
41+ try :
42+ func ()
43+ print ("\033 [92mSUCCESS\033 [0m" )
44+ return True
45+ except BaseException as e : # pylint: disable=broad-exception-caught
46+ print ("\033 [91mFAILED\033 [0m" )
47+ print ("--- Error ---" )
48+ print (str (e ))
49+ print ("--- Captured Output ---" )
50+ print (_out .getvalue ())
51+ return False
52+ finally :
53+ self .teardown ()
54+
2355
2456READABLE_DIR = "transpiler/javatests/com/google/j2cl/readable/java/emptyclass"
2557READABLE_DIR_KT = "transpiler/javatests/com/google/j2cl/readable/kotlin/emptyclass"
3668BUILD_FILE = os .path .join (READABLE_DIR , "BUILD" )
3769
3870
39- _out = io .StringIO ()
40- _backup = {}
41-
42-
43- def _setup ():
44- # Clear the buffer
45- _out .seek (0 )
46- _out .truncate (0 )
47-
48- _backup [GOLDEN_CLOSURE ] = open (GOLDEN_CLOSURE , "r" ).read ()
49- _backup [GOLDEN_WASM ] = open (GOLDEN_WASM , "r" ).read ()
50- _backup [GOLDEN_KT ] = open (GOLDEN_KT , "r" ).read ()
51- _backup [GOLDEN_KT_WEB ] = open (GOLDEN_KT_WEB , "r" ).read ()
52- _backup [SOURCE_FILE ] = open (SOURCE_FILE , "r" ).read ()
53- _backup [BUILD_FILE ] = open (BUILD_FILE , "r" ).read ()
54-
55- # Make sure we are starting with a clean state.
56- _ensure_clean_state ()
57-
58-
59- def _teardown ():
60- for path , content in _backup .items ():
61- with open (path , "w" ) as f :
62- f .write (content )
63-
64-
65- def _ensure_clean_state ():
66- if _out .getvalue ():
67- raise AssertionError ("Output buffer not cleared between tests!" )
68- localout = io .StringIO ()
69- with contextlib .redirect_stdout (localout ):
70- _run_gen ()
71- _assert_in ("Number of stale readables: 0" , localout .getvalue ())
71+ class GenValidationTest (ValidationTest ):
72+ """Validation tests for j2 gen."""
73+
74+ def setup (self ):
75+ super ().setup ()
76+
77+ self ._backup = {
78+ GOLDEN_CLOSURE : open (GOLDEN_CLOSURE , "r" ).read (),
79+ GOLDEN_WASM : open (GOLDEN_WASM , "r" ).read (),
80+ GOLDEN_KT : open (GOLDEN_KT , "r" ).read (),
81+ GOLDEN_KT_WEB : open (GOLDEN_KT_WEB , "r" ).read (),
82+ SOURCE_FILE : open (SOURCE_FILE , "r" ).read (),
83+ BUILD_FILE : open (BUILD_FILE , "r" ).read (),
84+ }
85+
86+ # Make sure we are starting with a clean state.
87+ self ._ensure_clean_state ()
88+
89+ def teardown (self ):
90+ super ().teardown ()
91+ for path , content in self ._backup .items ():
92+ with open (path , "w" ) as f :
93+ f .write (content )
94+
95+ def _ensure_clean_state (self ):
96+ if _out .getvalue ():
97+ raise AssertionError ("Output buffer not cleared between tests!" )
98+ localout = io .StringIO ()
99+ _j2 ("gen java/emptyclass" , out_stream = localout )
100+ _assert_in ("Number of stale readables: 0" , localout .getvalue ())
101+
102+ def test_golden_file_updates (self ):
103+ with open (GOLDEN_CLOSURE , "a" ) as f :
104+ f .write ("\n // STALE COMMENT\n " )
105+ with open (GOLDEN_WASM , "a" ) as f :
106+ f .write ("\n ;; STALE COMMENT\n " )
107+ with open (GOLDEN_KT , "a" ) as f :
108+ f .write ("\n // STALE COMMENT\n " )
109+ with open (GOLDEN_KT_WEB , "a" ) as f :
110+ f .write ("\n // STALE COMMENT\n " )
111+
112+ _j2 ("gen java/emptyclass" )
113+ _assert_output ("Number of stale readables: 4" )
114+
115+ with open (GOLDEN_CLOSURE , "r" ) as f :
116+ _assert_not_in ("// STALE COMMENT" , f .read ())
117+ with open (GOLDEN_WASM , "r" ) as f :
118+ _assert_not_in (";; STALE COMMENT" , f .read ())
119+ with open (GOLDEN_KT , "r" ) as f :
120+ _assert_not_in ("// STALE COMMENT" , f .read ())
121+ with open (GOLDEN_KT_WEB , "r" ) as f :
122+ _assert_not_in ("// STALE COMMENT" , f .read ())
123+
124+ def test_failed_compilation (self ):
125+ with open (SOURCE_FILE , "w" ) as f :
126+ f .write ("INVALID JAVA CODE" )
127+
128+ _j2_expecting_failure ("gen java/emptyclass" )
129+ _assert_output ("No test status for targets" )
130+ _assert_output ("Sponge link:" )
131+
132+ def test_broken_build_file (self ):
133+ with open (BUILD_FILE , "w" ) as f :
134+ f .write ("INVALID BAZEL SYNTAX" )
135+
136+ _j2_expecting_failure ("gen java/emptyclass" )
137+ _assert_output ("Error while running command" )
138+ _assert_output ("blaze query filter" )
139+
140+ def test_missing_file_in_srcs (self ):
141+ original_build = self ._backup [BUILD_FILE ]
142+ _assert_in ('glob(["*.java"])' , original_build )
143+
144+ broken_build = original_build .replace ('glob(["*.java"])' , "[]" )
145+ with open (BUILD_FILE , "w" ) as f :
146+ f .write (broken_build )
147+
148+ _j2_expecting_failure ("gen java/emptyclass" )
149+ _assert_output ("No test status for targets" )
150+ _assert_output ("Sponge link:" )
151+
152+ def test_platform_filtering (self ):
153+ _j2 ("-p CLOSURE gen java/emptyclass" )
154+ _assert_output ("Blaze building Wasm:\n No matches" )
155+
156+ def test_pattern (self ):
157+ _j2 ("-p CLOSURE gen empty.*" )
158+ _assert_not_output ("No matching readables!" )
159+ _assert_output (READABLE_DIR )
160+ _assert_output (READABLE_DIR_KT )
161+
162+ def test_pattern2 (self ):
163+ _j2 ("-p CLOSURE gen java/empty.*" )
164+ _assert_not_output ("No matching readables!" )
165+ _assert_output (READABLE_DIR )
166+ _assert_not_output (READABLE_DIR_KT )
167+
168+ def test_pattern3 (self ):
169+ _j2 ("-p CLOSURE gen kotlin/empty.*" )
170+ _assert_not_output ("No matching readables!" )
171+ _assert_not_output (READABLE_DIR )
172+ _assert_output (READABLE_DIR_KT )
173+
174+ def test_pattern_no_matches (self ):
175+ _j2 ("-p CLOSURE gen empty" )
176+ _assert_output ("No matching readables!" )
177+
178+
179+ class DiffValidationTest (ValidationTest ):
180+ """Validation tests for j2 diff."""
181+
182+
183+ def _j2 (args_str , out_stream = None ):
184+ if out_stream is None :
185+ out_stream = _out
186+ args = shlex .split (args_str )
187+ cmd = ["python3" , "dev/j2.py" ] + args
188+ result = subprocess .run (cmd , capture_output = True , text = True , check = False )
189+ out_stream .write (result .stdout )
190+ out_stream .write (result .stderr )
191+ if result .returncode != 0 :
192+ raise SystemExit (result .returncode )
193+
194+
195+ def _j2_expecting_failure (args_str ):
196+ try :
197+ _j2 (args_str )
198+ raise AssertionError (f"j2 gen { args_str } expected to fail but didn't." )
199+ except SystemExit :
200+ return
72201
73202
74203def _assert_in (needle , haystack , msg = None ):
@@ -81,150 +210,45 @@ def _assert_not_in(needle, haystack, msg=None):
81210 raise AssertionError (msg or f"Did not expect to find '{ needle } '." )
82211
83212
84- def _fail ():
85- raise AssertionError ("j2 gen expected to fail but didn't." )
86-
87-
88- def _run_gen (name = "java/emptyclass" , platforms = None ):
89- argv = argparse .Namespace (
90- readable_name = [name ], platforms = platforms or ["CLOSURE" , "WASM" , "J2KT" ]
91- )
92- replace_all .main (argv )
93-
94-
95- def _run_gen_expecting_failure (** kwargs ):
96- try :
97- _run_gen (** kwargs )
98- _fail ()
99- except SystemExit :
100- return
213+ def _assert_output (needle ):
214+ _assert_in (needle , _out .getvalue ())
101215
102216
103- def test_golden_file_updates ():
104- with open (GOLDEN_CLOSURE , "a" ) as f :
105- f .write ("\n // STALE COMMENT\n " )
106- with open (GOLDEN_WASM , "a" ) as f :
107- f .write ("\n ;; STALE COMMENT\n " )
108- with open (GOLDEN_KT , "a" ) as f :
109- f .write ("\n // STALE COMMENT\n " )
110- with open (GOLDEN_KT_WEB , "a" ) as f :
111- f .write ("\n // STALE COMMENT\n " )
217+ def _assert_not_output (needle ):
218+ _assert_not_in (needle , _out .getvalue ())
112219
113- _run_gen ()
114- output = _out .getvalue ()
115220
116- _assert_in ("Number of stale readables: 4" , output )
221+ def main (argv ):
222+ print ("Starting j2 validation...\n " )
117223
118- with open (GOLDEN_CLOSURE , "r" ) as f :
119- new_closure = f .read ()
120- with open (GOLDEN_WASM , "r" ) as f :
121- new_wasm = f .read ()
122- with open (GOLDEN_KT , "r" ) as f :
123- new_kt = f .read ()
124- with open (GOLDEN_KT_WEB , "r" ) as f :
125- new_kt_web = f .read ()
126-
127- _assert_not_in ("// STALE COMMENT" , new_closure , "Closure golden not updated!" )
128- _assert_not_in (";; STALE COMMENT" , new_wasm , "WASM golden not updated!" )
129- _assert_not_in ("// STALE COMMENT" , new_kt , "KT golden not updated!" )
130- _assert_not_in ("// STALE COMMENT" , new_kt_web , "J2KT Web golden not updated!" )
131-
132-
133- def test_failed_compilation ():
134- with open (SOURCE_FILE , "w" ) as f :
135- f .write ("INVALID JAVA CODE" )
136-
137- _run_gen_expecting_failure ()
138- _assert_in ("No test status for targets" , _out .getvalue ())
139- _assert_in ("Sponge link:" , _out .getvalue ())
140-
141-
142- def test_broken_build_file ():
143- with open (BUILD_FILE , "w" ) as f :
144- f .write ("INVALID BAZEL SYNTAX" )
145-
146- _run_gen_expecting_failure ()
147- _assert_in ("Error while running command" , _out .getvalue ())
148- _assert_in ("blaze query filter" , _out .getvalue ())
149-
150-
151- def test_missing_file_in_srcs ():
152- original_build = _backup [BUILD_FILE ]
153- _assert_in ('glob(["*.java"])' , original_build )
154-
155- broken_build = original_build .replace ('glob(["*.java"])' , "[]" )
156- with open (BUILD_FILE , "w" ) as f :
157- f .write (broken_build )
158-
159- _run_gen_expecting_failure ()
160- _assert_in ("No test status for targets" , _out .getvalue ())
161- _assert_in ("Sponge link:" , _out .getvalue ())
162-
163-
164- def test_platform_filtering ():
165- _run_gen (platforms = ["CLOSURE" ])
166- _assert_in ("Blaze building Wasm:\n No matches" , _out .getvalue ())
167-
168-
169- def test_pattern ():
170- _run_gen (name = "empty.*" , platforms = ["CLOSURE" ])
171- output = _out .getvalue ()
172- _assert_not_in ("No matching readables!" , output )
173- _assert_in (READABLE_DIR , output )
174- _assert_in (READABLE_DIR_KT , output )
175-
176-
177- def test_pattern2 ():
178- _run_gen (name = "java/empty.*" , platforms = ["CLOSURE" ])
179- output = _out .getvalue ()
180- _assert_not_in ("No matching readables!" , output )
181- _assert_in (READABLE_DIR , output )
182- _assert_not_in (READABLE_DIR_KT , output )
183-
184-
185- def test_pattern3 ():
186- _run_gen (name = "kotlin/empty.*" , platforms = ["CLOSURE" ])
187- output = _out .getvalue ()
188- _assert_not_in ("No matching readables!" , output )
189- _assert_not_in (READABLE_DIR , output )
190- _assert_in (READABLE_DIR_KT , output )
191-
192-
193- def test_pattern_no_matches ():
194- _run_gen (name = "empty" , platforms = ["CLOSURE" ])
195- output = _out .getvalue ()
196- _assert_in ("No matching readables!" , output )
197-
198-
199- def _run_test (name , func ):
200- print (f"{ name } : " , end = "" , flush = True )
201- _setup ()
202- try :
203- with contextlib .redirect_stdout (_out ):
204- func ()
205- print ("\033 [92mSUCCESS\033 [0m" )
206- return True
207- except BaseException as e : # pylint: disable=broad-exception-caught
208- print ("\033 [91mFAILED\033 [0m" )
209- print ("--- Error ---" )
210- print (str (e ))
211- print ("--- Captured Output ---" )
212- print (_out .getvalue ())
213- return False
214- finally :
215- _teardown ()
216-
217-
218- def main (unused_argv ):
219- print ("Starting j2 gen validation...\n " )
224+ test_suites_mapping = {
225+ "gen" : GenValidationTest (),
226+ "diff" : DiffValidationTest (),
227+ }
228+ test_suites = (
229+ [test_suites_mapping [argv .test_suite ]]
230+ if argv .test_suite
231+ else test_suites_mapping .values ()
232+ )
220233
221234 success = True
222- for name , func in list (globals ().items ()):
223- if not name .startswith ("test_" ):
224- continue
225- display_name = name .replace ("_" , " " ).capitalize ()
226- success &= _run_test (display_name , func )
235+ for test_suite in test_suites :
236+ for name in dir (test_suite ):
237+ if not name .startswith ("test_" ):
238+ continue
239+ func = getattr (test_suite , name )
240+ display_name = name .replace ("_" , " " ).capitalize ()
241+ success &= test_suite .run_test (display_name , func )
227242
228243 print ("" )
229244 if not success :
230245 sys .exit (1 )
246+
247+
248+ def add_arguments (parser ):
249+ parser .add_argument (
250+ "test_suite" ,
251+ nargs = "?" ,
252+ choices = ["gen" , "diff" ],
253+ help = "Test suite to run (gen or diff). If omitted, all tests are run." ,
254+ )
0 commit comments