psdi-data-conversion 0.0.36__py3-none-any.whl → 0.0.38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,19 +19,56 @@ from unittest.mock import patch
19
19
  import py
20
20
  import pytest
21
21
 
22
+ import psdi_data_conversion
22
23
  from psdi_data_conversion.constants import CONVERTER_DEFAULT, GLOBAL_LOG_FILENAME, LOG_NONE, OUTPUT_LOG_EXT
23
24
  from psdi_data_conversion.converter import run_converter
24
25
  from psdi_data_conversion.converters.openbabel import COORD_GEN_KEY, COORD_GEN_QUAL_KEY
26
+ from psdi_data_conversion.database import get_format_info
25
27
  from psdi_data_conversion.dist import LINUX_LABEL, get_dist
26
28
  from psdi_data_conversion.file_io import is_archive, split_archive_ext
27
29
  from psdi_data_conversion.main import main as data_convert_main
28
- from psdi_data_conversion.testing.constants import INPUT_TEST_DATA_LOC
30
+ from psdi_data_conversion.testing.constants import (INPUT_TEST_DATA_LOC_IN_PROJECT, OUTPUT_TEST_DATA_LOC_IN_PROJECT,
31
+ TEST_DATA_LOC_IN_PROJECT)
32
+
33
+
34
+ def get_path_in_project(filename):
35
+ """Get the realpath to a file contained within the project, given its project-relative path"""
36
+
37
+ old_cwd = os.getcwd()
38
+
39
+ try:
40
+ os.chdir(os.path.join(psdi_data_conversion.__path__[0], ".."))
41
+ realpath = os.path.realpath(filename)
42
+
43
+ finally:
44
+ # Change back to the previous directory
45
+ os.chdir(old_cwd)
46
+
47
+ return realpath
48
+
49
+
50
+ def get_test_data_loc():
51
+ """Get the realpath of the base directory containing all data for tests"""
52
+ return get_path_in_project(TEST_DATA_LOC_IN_PROJECT)
53
+
54
+
55
+ def get_input_test_data_loc():
56
+ """Get the realpath of the base directory containing input data for tests"""
57
+ return get_path_in_project(INPUT_TEST_DATA_LOC_IN_PROJECT)
58
+
59
+
60
+ def get_output_test_data_loc():
61
+ """Get the realpath of the base directory containing expected output data for tests"""
62
+ return get_path_in_project(OUTPUT_TEST_DATA_LOC_IN_PROJECT)
29
63
 
30
64
 
31
65
  @dataclass
32
66
  class ConversionTestInfo:
33
67
  """Information about a tested conversion."""
34
68
 
69
+ run_type: str
70
+ """One of "library", "cla", or "gui", describing which type of test run was performed"""
71
+
35
72
  test_spec: SingleConversionTestSpec
36
73
  """The specification of the test conversion which was run to produce this"""
37
74
 
@@ -50,20 +87,23 @@ class ConversionTestInfo:
50
87
  captured_stderr: str | None = None
51
88
  """Any output to stderr while the test was run"""
52
89
 
90
+ exc_info: pytest.ExceptionInfo | None = None
91
+ """If the test conversion raised an exception, that exception's info, otherwise None"""
92
+
53
93
  @property
54
94
  def qualified_in_filename(self):
55
95
  """Get the fully-qualified name of the input file"""
56
- return os.path.join(self.input_dir, self.test_spec.filename)
96
+ return os.path.realpath(os.path.join(self.input_dir, self.test_spec.filename))
57
97
 
58
98
  @property
59
99
  def qualified_out_filename(self):
60
100
  """Get the fully-qualified name of the output file"""
61
- return os.path.join(self.output_dir, self.test_spec.out_filename)
101
+ return os.path.realpath(os.path.join(self.output_dir, self.test_spec.out_filename))
62
102
 
63
103
  @property
64
104
  def qualified_log_filename(self):
65
105
  """Get the fully-qualified name of the log file"""
66
- return os.path.join(self.output_dir, self.test_spec.log_filename)
106
+ return os.path.realpath(os.path.join(self.output_dir, self.test_spec.log_filename))
67
107
 
68
108
  @property
69
109
  def qualified_global_log_filename(self):
@@ -71,28 +111,6 @@ class ConversionTestInfo:
71
111
  return self.test_spec.global_log_filename
72
112
 
73
113
 
74
- @dataclass
75
- class LibraryConversionTestInfo(ConversionTestInfo):
76
- """Information about a tested conversion, specifically for when it was tested through a call to the library"""
77
-
78
- exc_info: pytest.ExceptionInfo | None = None
79
- """If the test conversion raised an exception, that exception's info, otherwise None"""
80
-
81
-
82
- @dataclass
83
- class CLAConversionTestInfo(ConversionTestInfo):
84
- """Information about a tested conversion, specifically for when it was tested through a the command-line
85
- application (CLA)
86
- """
87
-
88
-
89
- @dataclass
90
- class GUIConversionTestInfo(ConversionTestInfo):
91
- """Information about a tested conversion, specifically for when it was tested through the GUI (the local version of
92
- the web app)
93
- """
94
-
95
-
96
114
  @dataclass
97
115
  class ConversionTestSpec:
98
116
  """Class providing a specification for a test file conversion.
@@ -108,9 +126,12 @@ class ConversionTestSpec:
108
126
  filename: str | Iterable[str] = "nacl.cif"
109
127
  """The name of the input file, relative to the input test data location, or a list thereof"""
110
128
 
111
- to_format: str | Iterable[str] = "pdb"
129
+ to_format: str | int | Iterable[str | int] = "pdb"
112
130
  """The format to test converting the input file to, or a list thereof"""
113
131
 
132
+ from_format: str | int | Iterable[str | int] | None = None
133
+ """The format of the input file, when it needs to be explicitly specified"""
134
+
114
135
  converter_name: str | Iterable[str] = CONVERTER_DEFAULT
115
136
  """The name of the converter to be used for the test, or a list thereof"""
116
137
 
@@ -121,6 +142,11 @@ class ConversionTestSpec:
121
142
  expect_success: bool | Iterable[bool] = True
122
143
  """Whether or not to expect the test to succeed"""
123
144
 
145
+ skip: bool | Iterable[bool] = False
146
+ """If set to true, this test will be skipped and not run. Can also be set individually for certain tests within an
147
+ array. This should typically only be used when debugging to skip working tests to more easily focus on non-working
148
+ tests"""
149
+
124
150
  callback: (Callable[[ConversionTestInfo], str] |
125
151
  Iterable[Callable[[ConversionTestInfo], str]] | None) = None
126
152
  """Function to be called after the conversion is performed to check in detail whether results are as expected. It
@@ -187,6 +213,9 @@ class ConversionTestSpec:
187
213
  if attr_name in l_single_val_attrs:
188
214
  setattr(self, attr_name, [getattr(self, attr_name)]*self._len)
189
215
 
216
+ # Check if all tests should be skipped
217
+ self.skip_all = all(self.skip)
218
+
190
219
  def __len__(self):
191
220
  """Get the length from the member - valid only after `__post_init__` has been called"""
192
221
  return self._len
@@ -208,9 +237,12 @@ class SingleConversionTestSpec:
208
237
  filename: str
209
238
  """The name of the input file, relative to the input test data location"""
210
239
 
211
- to_format: str
240
+ to_format: str | int
212
241
  """The format to test converting the input file to"""
213
242
 
243
+ from_format: str | int | None = None
244
+ """The format of the input file, when it needs to be explicitly specified"""
245
+
214
246
  converter_name: str | Iterable[str] = CONVERTER_DEFAULT
215
247
  """The name of the converter to be used for the test"""
216
248
 
@@ -221,6 +253,9 @@ class SingleConversionTestSpec:
221
253
  expect_success: bool = True
222
254
  """Whether or not to expect the test to succeed"""
223
255
 
256
+ skip: bool = False
257
+ """If set to True, this test will be skipped, always returning success"""
258
+
224
259
  callback: (Callable[[ConversionTestInfo], str] | None) = None
225
260
  """Function to be called after the conversion is performed to check in detail whether results are as expected. It
226
261
  should take as its only argument a `ConversionTestInfo` and return a string. The string should be empty if the check
@@ -229,11 +264,12 @@ class SingleConversionTestSpec:
229
264
  @property
230
265
  def out_filename(self) -> str:
231
266
  """The unqualified name of the output file which should have been created by the conversion."""
267
+ to_format_name = get_format_info(self.to_format, which=0).name
232
268
  if not is_archive(self.filename):
233
- return f"{os.path.splitext(self.filename)[0]}.{self.to_format}"
269
+ return f"{os.path.splitext(self.filename)[0]}.{to_format_name}"
234
270
  else:
235
271
  filename_base, ext = split_archive_ext(os.path.basename(self.filename))
236
- return f"{filename_base}-{self.to_format}{ext}"
272
+ return f"{filename_base}-{to_format_name}{ext}"
237
273
 
238
274
  @property
239
275
  def log_filename(self) -> str:
@@ -258,9 +294,14 @@ def run_test_conversion_with_library(test_spec: ConversionTestSpec):
258
294
  with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
259
295
  # Iterate over the test spec to run each individual test it defines
260
296
  for single_test_spec in test_spec:
297
+ if single_test_spec.skip:
298
+ print(f"Skipping single test spec {single_test_spec}")
299
+ continue
300
+ print(f"Running single test spec: {single_test_spec}")
261
301
  _run_single_test_conversion_with_library(test_spec=single_test_spec,
262
302
  input_dir=input_dir,
263
303
  output_dir=output_dir)
304
+ print(f"Success for test spec: {single_test_spec}")
264
305
 
265
306
 
266
307
  def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec,
@@ -279,9 +320,9 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
279
320
  """
280
321
 
281
322
  # Symlink the input file to the input directory
282
- qualified_in_filename = os.path.join(input_dir, test_spec.filename)
323
+ qualified_in_filename = os.path.realpath(os.path.join(input_dir, test_spec.filename))
283
324
  try:
284
- os.symlink(os.path.join(INPUT_TEST_DATA_LOC, test_spec.filename),
325
+ os.symlink(os.path.join(get_input_test_data_loc(), test_spec.filename),
285
326
  qualified_in_filename)
286
327
  except FileExistsError:
287
328
  pass
@@ -294,6 +335,7 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
294
335
  if test_spec.expect_success:
295
336
  run_converter(filename=qualified_in_filename,
296
337
  to_format=test_spec.to_format,
338
+ from_format=test_spec.from_format,
297
339
  name=test_spec.converter_name,
298
340
  upload_dir=input_dir,
299
341
  download_dir=output_dir,
@@ -303,6 +345,7 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
303
345
  with pytest.raises(Exception) as exc_info:
304
346
  run_converter(filename=qualified_in_filename,
305
347
  to_format=test_spec.to_format,
348
+ from_format=test_spec.from_format,
306
349
  name=test_spec.converter_name,
307
350
  upload_dir=input_dir,
308
351
  download_dir=output_dir,
@@ -316,15 +359,17 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
316
359
 
317
360
  # Compile output info for the test and call the callback function if one is provided
318
361
  if test_spec.callback:
319
- test_info = LibraryConversionTestInfo(test_spec=test_spec,
320
- input_dir=input_dir,
321
- output_dir=output_dir,
322
- success=success,
323
- captured_stdout=stdout,
324
- captured_stderr=stderr,
325
- exc_info=exc_info)
362
+ test_info = ConversionTestInfo(run_type="library",
363
+ test_spec=test_spec,
364
+ input_dir=input_dir,
365
+ output_dir=output_dir,
366
+ success=success,
367
+ captured_stdout=stdout,
368
+ captured_stderr=stderr,
369
+ exc_info=exc_info)
326
370
  callback_msg = test_spec.callback(test_info)
327
- assert not callback_msg, callback_msg
371
+ if callback_msg:
372
+ pytest.fail(callback_msg)
328
373
 
329
374
 
330
375
  def run_test_conversion_with_cla(test_spec: ConversionTestSpec):
@@ -339,9 +384,14 @@ def run_test_conversion_with_cla(test_spec: ConversionTestSpec):
339
384
  with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
340
385
  # Iterate over the test spec to run each individual test it defines
341
386
  for single_test_spec in test_spec:
387
+ if single_test_spec.skip:
388
+ print(f"Skipping single test spec {single_test_spec}")
389
+ continue
390
+ print(f"Running single test spec: {single_test_spec}")
342
391
  _run_single_test_conversion_with_cla(test_spec=single_test_spec,
343
392
  input_dir=input_dir,
344
393
  output_dir=output_dir)
394
+ print(f"Success for test spec: {single_test_spec}")
345
395
 
346
396
 
347
397
  def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
@@ -360,9 +410,9 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
360
410
  """
361
411
 
362
412
  # Symlink the input file to the input directory
363
- qualified_in_filename = os.path.join(input_dir, test_spec.filename)
413
+ qualified_in_filename = os.path.realpath(os.path.join(input_dir, test_spec.filename))
364
414
  try:
365
- os.symlink(os.path.join(INPUT_TEST_DATA_LOC, test_spec.filename),
415
+ os.symlink(os.path.join(get_input_test_data_loc(), test_spec.filename),
366
416
  qualified_in_filename)
367
417
  except FileExistsError:
368
418
  pass
@@ -374,6 +424,7 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
374
424
  if test_spec.expect_success:
375
425
  run_converter_through_cla(filename=qualified_in_filename,
376
426
  to_format=test_spec.to_format,
427
+ from_format=test_spec.from_format,
377
428
  name=test_spec.converter_name,
378
429
  input_dir=input_dir,
379
430
  output_dir=output_dir,
@@ -384,6 +435,7 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
384
435
  with pytest.raises(SystemExit) as exc_info:
385
436
  run_converter_through_cla(filename=qualified_in_filename,
386
437
  to_format=test_spec.to_format,
438
+ from_format=test_spec.from_format,
387
439
  name=test_spec.converter_name,
388
440
  input_dir=input_dir,
389
441
  output_dir=output_dir,
@@ -392,7 +444,7 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
392
444
  # Get the success from whether or not the exit code is 0
393
445
  success = not exc_info.value.code
394
446
 
395
- qualified_out_filename = os.path.join(output_dir, test_spec.out_filename)
447
+ qualified_out_filename = os.path.realpath(os.path.join(output_dir, test_spec.out_filename))
396
448
 
397
449
  # Determine success based on whether or not the output file exists with non-zero size
398
450
  if not os.path.isfile(qualified_out_filename) or os.path.getsize(qualified_out_filename) == 0:
@@ -405,14 +457,16 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
405
457
 
406
458
  # Compile output info for the test and call the callback function if one is provided
407
459
  if test_spec.callback:
408
- test_info = CLAConversionTestInfo(test_spec=test_spec,
409
- input_dir=input_dir,
410
- output_dir=output_dir,
411
- success=success,
412
- captured_stdout=stdout,
413
- captured_stderr=stderr)
460
+ test_info = ConversionTestInfo(run_type="cla",
461
+ test_spec=test_spec,
462
+ input_dir=input_dir,
463
+ output_dir=output_dir,
464
+ success=success,
465
+ captured_stdout=stdout,
466
+ captured_stderr=stderr)
414
467
  callback_msg = test_spec.callback(test_info)
415
- assert not callback_msg, callback_msg
468
+ if callback_msg:
469
+ pytest.fail(callback_msg)
416
470
 
417
471
 
418
472
  def run_converter_through_cla(filename: str,
@@ -421,6 +475,7 @@ def run_converter_through_cla(filename: str,
421
475
  input_dir: str,
422
476
  output_dir: str,
423
477
  log_file: str,
478
+ from_format: str | None = None,
424
479
  **conversion_kwargs):
425
480
  """Runs a test conversion through the command-line interface
426
481
 
@@ -441,17 +496,23 @@ def run_converter_through_cla(filename: str,
441
496
  The directory which contains the output file
442
497
  log_file : str
443
498
  The desired name of the log file
499
+ conversion_kwargs : Any
500
+ Additional arguments describing the conversion
501
+ from_format : str | None
502
+ The format of the input file, when it needs to be explicitly specified, otherwise None
444
503
  """
445
504
 
446
505
  # Start the argument string with the arguments we will always include
447
506
  arg_string = f"{filename} -i {input_dir} -t {to_format} -o {output_dir} -w {name} --log-file {log_file}"
448
507
 
449
- # For each argument in the conversion kwargs, convert it to the appropriate argument to be provided to the
450
- # argument string
508
+ # For from_format and each argument in the conversion kwargs, convert it to the appropriate argument to be provided
509
+ # to the argument string
510
+
511
+ if from_format:
512
+ arg_string += f" -f {from_format}"
513
+
451
514
  for key, val in conversion_kwargs.items():
452
- if key == "from_format":
453
- arg_string += f" -f {val}"
454
- elif key == "log_mode":
515
+ if key == "log_mode":
455
516
  if val == LOG_NONE:
456
517
  arg_string += " -q"
457
518
  else:
@@ -464,8 +525,8 @@ def run_converter_through_cla(filename: str,
464
525
  arg_string += " --strict"
465
526
  elif key == "max_file_size":
466
527
  if val != 0:
467
- assert False, ("Test specification imposes a maximum file size, which isn't compatible with the "
468
- "command-line application.")
528
+ pytest.fail("Test specification imposes a maximum file size, which isn't compatible with the "
529
+ "command-line application.")
469
530
  elif key == "data":
470
531
  for subkey, subval in val.items():
471
532
  if subkey == "from_flags":
@@ -484,10 +545,10 @@ def run_converter_through_cla(filename: str,
484
545
  # Handled alongside COORD_GEN_KEY above
485
546
  pass
486
547
  else:
487
- assert False, (f"The key 'data[\"{subkey}\"]' was passed to `conversion_kwargs` but could not be "
488
- "interpreted")
548
+ pytest.fail(f"The key 'data[\"{subkey}\"]' was passed to `conversion_kwargs` but could not be "
549
+ "interpreted")
489
550
  else:
490
- assert False, f"The key '{key}' was passed to `conversion_kwargs` but could not be interpreted"
551
+ pytest.fail(f"The key '{key}' was passed to `conversion_kwargs` but could not be interpreted")
491
552
 
492
553
  run_with_arg_string(arg_string)
493
554