51
51
from packaging .specifiers import SpecifierSet
52
52
from packaging .version import Version , parse
53
53
from firebird .driver import connect , connect_server , create_database , driver_config , \
54
- NetProtocol , PageSize , Server
54
+ NetProtocol , PageSize , Server , CHARSET_MAP
55
55
56
56
_vars_ = {'server' : None ,
57
57
'bin-dir' : None ,
@@ -144,22 +144,29 @@ def pytest_configure(config):
144
144
set_tool (tool )
145
145
146
146
147
- def pytest_collection_modifyitems (config , items ):
147
+ def pytest_collection_modifyitems (session , config , items ):
148
148
skip_slow = pytest .mark .skip (reason = "need --runslow option to run" )
149
149
skip_platform = pytest .mark .skip (reason = f"test not designed for { _platform } " )
150
- skip_version = pytest . mark . skip ( reason = f"test not designed for { _vars_ [ 'version' ] } " )
150
+ # Apply skip markers
151
151
for item in items :
152
152
if 'slow' in item .keywords and not _vars_ ['runslow' ]:
153
153
item .add_marker (skip_slow )
154
- platforms = [mark .args for mark in item .iter_markers (name = "platform" )]
155
- for items in platforms :
156
- if _platform not in items :
154
+ for platforms in [mark .args for mark in item .iter_markers (name = "platform" )]:
155
+ if _platform not in platforms :
157
156
item .add_marker (skip_platform )
157
+ # Deselect tests not applicable to tested engine version
158
+ selected = []
159
+ deselected = []
160
+ for item in items :
158
161
versions = [mark .args for mark in item .iter_markers (name = "version" )]
159
162
if versions :
160
163
spec = SpecifierSet (',' .join (list (versions [0 ])))
161
- if _vars_ ['version' ] not in spec :
162
- item .add_marker (skip_version )
164
+ if _vars_ ['version' ] in spec :
165
+ selected .append (item )
166
+ else :
167
+ deselected .append (item )
168
+ items [:] = selected
169
+ config .hook .pytest_deselected (items = deselected )
163
170
164
171
@pytest .fixture (autouse = True )
165
172
def firebird_server ():
@@ -183,8 +190,8 @@ def __init__(self, path: Path, filename: str='test.fdb',
183
190
self .dsn = f"{ _vars_ ['host' ]} :{ str (self .db_path )} "
184
191
else :
185
192
self .dsn = str (self .db_path )
186
- self .subs = {'temp_directory' : str (path ) , 'database_location' : str (path ) ,
187
- 'DATABASE_PATH' : str (path ) , 'DSN' : self .dsn ,
193
+ self .subs = {'temp_directory' : str (path / 'x' )[: - 1 ] , 'database_location' : str (path / 'x' )[: - 1 ] ,
194
+ 'DATABASE_PATH' : str (path / 'x' )[: - 1 ] , 'DSN' : self .dsn ,
188
195
'files_location' : str (_vars_ ['root' ] / 'files' ),
189
196
'backup_location' : str (_vars_ ['root' ] / 'backups' ),
190
197
'suite_database_location' : str (_vars_ ['root' ] / 'databases' ),
@@ -210,7 +217,7 @@ def _make_config(self, page_size: int=None, sql_dialect: int=None, charset: str=
210
217
def create (self , page_size : int = None , sql_dialect : int = None , charset : str = None ) -> None :
211
218
#__tracebackhide__ = True
212
219
self ._make_config (page_size , sql_dialect , charset )
213
- # print(f"Creating db: {self.db_path} [{page_size=}, {sql_dialect=}, {charset=}, user={self.user}, password={self.password}]")
220
+ print (f"Creating db: { self .db_path } [{ page_size = } , { sql_dialect = } , { charset = } , user={ self .user } , password={ self .password } ]" )
214
221
db = create_database ('pytest' )
215
222
db .close ()
216
223
def restore (self , backup : str ) -> None :
@@ -227,7 +234,7 @@ def restore(self, backup: str) -> None:
227
234
print (result .stdout )
228
235
print (f"-- stderr { '-' * 20 } " )
229
236
print (result .stderr )
230
- raise CalledProcessError ( result . returncode , result . args , result . stdout , result . stderr )
237
+ raise Exception ( "Database restore failed" )
231
238
# Fix permissions
232
239
#if platform.system != 'Windows':
233
240
#os.chmod(self.db_path, 16895)
@@ -252,21 +259,25 @@ def init(self, script: str) -> CompletedProcess:
252
259
print (result .stdout )
253
260
print (f"-- stderr { '-' * 20 } " )
254
261
print (result .stderr )
255
- raise CalledProcessError ( result . returncode , result . args , result . stdout , result . stderr )
262
+ raise Exception ( "Database init script execution failed" )
256
263
return result
257
- def execute (self , script : str , * , raise_on_fail : bool ) -> CompletedProcess :
264
+ def execute (self , script : str , * , raise_on_fail : bool , charset : str = 'utf8' ) -> CompletedProcess :
258
265
__tracebackhide__ = True
259
266
#print("Running test script")
260
- result = run ([_vars_ ['isql' ], '-ch' , 'utf8' , '-user' , self .user ,
267
+ if charset :
268
+ charset = charset .upper ()
269
+ else :
270
+ charset = 'NONE'
271
+ result = run ([_vars_ ['isql' ], '-ch' , charset , '-user' , self .user ,
261
272
'-password' , self .password , str (self .dsn )],
262
273
input = substitute_macros (script , self .subs ),
263
- encoding = 'utf8' , capture_output = True )
274
+ encoding = CHARSET_MAP [ charset ] , capture_output = True )
264
275
if result .returncode and raise_on_fail :
265
276
print (f"-- stdout { '-' * 20 } " )
266
277
print (result .stdout )
267
278
print (f"-- stderr { '-' * 20 } " )
268
279
print (result .stderr )
269
- raise CalledProcessError ( result . returncode , result . args , result . stdout , result . stderr )
280
+ raise Exception ( "ISQL script execution failed" )
270
281
return result
271
282
def drop (self ) -> None :
272
283
self ._make_config ()
@@ -332,9 +343,11 @@ def database_fixture(request: FixtureRequest, db_path) -> Database:
332
343
return database_fixture
333
344
334
345
class Action :
335
- def __init__ (self , db : Database , script : str , substitutions : List [str ]):
346
+ def __init__ (self , db : Database , script : str , substitutions : List [str ], outfile : Path ,
347
+ charset : str ):
336
348
self .db : Database = db
337
349
self .script : str = script
350
+ self .charset : str = charset
338
351
self .return_code : int = 0
339
352
self .stdout : str = ''
340
353
self ._clean_stdout : str = None
@@ -345,6 +358,7 @@ def __init__(self, db: Database, script: str, substitutions: List[str]):
345
358
self .expected_stderr : str = ''
346
359
self ._clean_expected_stderr : str = None
347
360
self .substitutions : List [str ] = [x for x in substitutions ]
361
+ self .outfile : Path = outfile
348
362
def make_diff (self , left : str , right : str ) -> str :
349
363
return '\n ' .join (difflib .ndiff (left .splitlines (), right .splitlines ()))
350
364
def space_strip (self , value : str ) -> str :
@@ -369,11 +383,23 @@ def string_strip(self, value: str, substitutions: List[str]=[], isql: bool=True,
369
383
return value
370
384
def execute (self ) -> None :
371
385
__tracebackhide__ = True
386
+ out_file : Path = self .outfile .with_suffix ('.out' )
387
+ err_file : Path = self .outfile .with_suffix ('.err' )
388
+ if out_file .is_file ():
389
+ out_file .unlink ()
390
+ if err_file .is_file ():
391
+ err_file .unlink ()
372
392
result : CompletedProcess = self .db .execute (self .script ,
373
- raise_on_fail = not bool (self .expected_stderr ))
393
+ raise_on_fail = not bool (self .expected_stderr ),
394
+ charset = self .charset )
374
395
self .return_code : int = result .returncode
375
396
self .stdout : str = result .stdout
376
397
self .stderr : str = result .stderr
398
+ # Store output
399
+ if self .stdout :
400
+ out_file .write_text (self .stdout )
401
+ if self .stderr :
402
+ err_file .write_text (self .stderr )
377
403
@property
378
404
def clean_stdout (self ) -> str :
379
405
if self ._clean_stdout is None :
@@ -395,12 +421,18 @@ def clean_expected_stderr(self) -> str:
395
421
self ._clean_expected_stderr = self .string_strip (self .expected_stderr , self .substitutions )
396
422
return self ._clean_expected_stderr
397
423
398
- def isql_act (db_fixture_name : str , script : str , * , substitutions : List [str ]= None ):
424
+ def isql_act (db_fixture_name : str , script : str , * , substitutions : List [str ]= None ,
425
+ charset : str = 'utf8' ):
399
426
400
427
@pytest .fixture
401
428
def isql_act_fixture (request : FixtureRequest ) -> Action :
402
429
db : Database = request .getfixturevalue (db_fixture_name )
403
- result : Action = Action (db , script , substitutions )
430
+ f : Path = Path .cwd () / 'out' / request .module .__name__ .replace ('.' , '/' )
431
+ if not f .parent .exists ():
432
+ f .parent .mkdir (parents = True )
433
+ f = f .with_name (f'{ f .stem } -{ request .function .__name__ } .out' )
434
+ #f.write_text('stdout')
435
+ result : Action = Action (db , script , substitutions , f , charset )
404
436
return result
405
437
406
438
return isql_act_fixture
0 commit comments