psdi-data-conversion 0.0.36__py3-none-any.whl → 0.0.37__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.
@@ -0,0 +1,366 @@
1
+ """
2
+ # gui.py
3
+
4
+ Utilities to aid in testing of the GUI
5
+ """
6
+
7
+
8
+ import os
9
+ import shutil
10
+ from tempfile import TemporaryDirectory
11
+
12
+ import time
13
+ import pytest
14
+ from selenium.common.exceptions import TimeoutException
15
+ from selenium.webdriver.common.alert import Alert
16
+ from selenium.webdriver.common.by import By
17
+ from selenium.webdriver.firefox.webdriver import WebDriver
18
+ from selenium.webdriver.support import expected_conditions as EC
19
+ from selenium.webdriver.support.select import Select
20
+ from selenium.webdriver.support.ui import WebDriverWait
21
+
22
+ from psdi_data_conversion.constants import STATUS_CODE_GENERAL
23
+ from psdi_data_conversion.converters.base import (FileConverterAbortException, FileConverterException,
24
+ FileConverterInputException)
25
+ from psdi_data_conversion.converters.openbabel import (COORD_GEN_KEY, COORD_GEN_QUAL_KEY, DEFAULT_COORD_GEN_QUAL,
26
+ L_ALLOWED_COORD_GEN_QUALS, L_ALLOWED_COORD_GENS)
27
+ from psdi_data_conversion.file_io import split_archive_ext
28
+ from psdi_data_conversion.testing.utils import (ConversionTestInfo, ConversionTestSpec, SingleConversionTestSpec,
29
+ get_input_test_data_loc)
30
+
31
+ # Standard timeout at 10 seconds
32
+ TIMEOUT = 10
33
+
34
+
35
+ def wait_for_element(root: WebDriver | EC.WebElement, xpath: str, by=By.XPATH):
36
+ """Shortcut for boilerplate to wait until a web element is visible"""
37
+ WebDriverWait(root, TIMEOUT).until(EC.element_to_be_clickable((by, xpath)))
38
+
39
+
40
+ def wait_and_find_element(root: WebDriver | EC.WebElement, xpath: str, by=By.XPATH) -> EC.WebElement:
41
+ """Finds a web element, after first waiting to ensure it's visible"""
42
+ wait_for_element(root, xpath, by=by)
43
+ return root.find_element(by, xpath)
44
+
45
+
46
+ def run_test_conversion_with_gui(test_spec: ConversionTestSpec,
47
+ driver: WebDriver,
48
+ origin: str):
49
+ """Runs a test conversion or series thereof through the GUI. Note that this requires the server to be started before
50
+ this is called.
51
+
52
+ Parameters
53
+ ----------
54
+ test_spec : ConversionTestSpec
55
+ The specification for the test or series of tests to be run
56
+ driver : WebDriver
57
+ The WebDriver to be used for testing
58
+ origin : str
59
+ The address of the homepage of the testing server
60
+ """
61
+ # Make temporary directories for the input and output files to be stored in
62
+ with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
63
+ # Iterate over the test spec to run each individual test it defines
64
+ for single_test_spec in test_spec:
65
+ if single_test_spec.skip:
66
+ print(f"Skipping single test spec {single_test_spec}")
67
+ continue
68
+ print(f"Running single test spec: {single_test_spec}")
69
+ _run_single_test_conversion_with_gui(test_spec=single_test_spec,
70
+ input_dir=input_dir,
71
+ output_dir=output_dir,
72
+ driver=driver,
73
+ origin=origin)
74
+ print(f"Success for test spec: {single_test_spec}")
75
+
76
+
77
+ def _run_single_test_conversion_with_gui(test_spec: SingleConversionTestSpec,
78
+ input_dir: str,
79
+ output_dir: str,
80
+ driver: WebDriver,
81
+ origin: str):
82
+ """Runs a single test conversion through the GUI.
83
+
84
+ Parameters
85
+ ----------
86
+ test_spec : SingleConversionTestSpec
87
+ The specification for the test to be run
88
+ input_dir : str
89
+ A directory which can be used to store input data before uploading
90
+ output_dir : str
91
+ A directory which can be used to create output data after downloading
92
+ driver : WebDriver
93
+ The WebDriver to be used for testing
94
+ origin : str
95
+ The address of the homepage of the testing server
96
+ """
97
+
98
+ exc_info: pytest.ExceptionInfo | None = None
99
+ if test_spec.expect_success:
100
+ try:
101
+ run_converter_through_gui(test_spec=test_spec,
102
+ input_dir=input_dir,
103
+ output_dir=output_dir,
104
+ driver=driver,
105
+ origin=origin,
106
+ **test_spec.conversion_kwargs)
107
+ success = True
108
+ except Exception:
109
+ print(f"Unexpected exception raised for single test spec {test_spec}")
110
+ raise
111
+ else:
112
+ with pytest.raises(FileConverterException) as exc_info:
113
+ run_converter_through_gui(test_spec=test_spec,
114
+ input_dir=input_dir,
115
+ output_dir=output_dir,
116
+ driver=driver,
117
+ origin=origin,
118
+ **test_spec.conversion_kwargs)
119
+ success = False
120
+
121
+ # Compile output info for the test and call the callback function if one is provided
122
+ if test_spec.callback:
123
+ test_info = ConversionTestInfo(run_type="gui",
124
+ test_spec=test_spec,
125
+ input_dir=input_dir,
126
+ output_dir=output_dir,
127
+ success=success,
128
+ exc_info=exc_info)
129
+ callback_msg = test_spec.callback(test_info)
130
+ assert not callback_msg, callback_msg
131
+
132
+
133
+ def run_converter_through_gui(test_spec: SingleConversionTestSpec,
134
+ input_dir: str,
135
+ output_dir: str,
136
+ driver: WebDriver,
137
+ origin: str,
138
+ **conversion_kwargs):
139
+ """_summary_
140
+
141
+ Parameters
142
+ ----------
143
+ test_spec : SingleConversionTestSpec
144
+ The specification for the test to be run
145
+ input_dir : str
146
+ The directory which contains the input file
147
+ output_dir : str
148
+ The directory which contains the output file
149
+ driver : WebDriver
150
+ The WebDriver to be used for testing
151
+ origin : str
152
+ The address of the homepage of the testing server
153
+ """
154
+
155
+ # Get just the local filename
156
+ filename = os.path.split(test_spec.filename)[1]
157
+
158
+ # Default options for conversion
159
+ base_filename, from_format = split_archive_ext(filename)
160
+ strict = True
161
+ from_flags: str | None = None
162
+ to_flags: str | None = None
163
+ from_options: str | None = None
164
+ to_options: str | None = None
165
+ coord_gen = None
166
+ coord_gen_qual = None
167
+
168
+ # For each argument in the conversion kwargs, interpret it as the appropriate option for this conversion, overriding
169
+ # defaults set above
170
+ for key, val in conversion_kwargs.items():
171
+ if key == "from_format":
172
+ from_format = val
173
+ elif key == "log_mode":
174
+ raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
175
+ elif key == "delete_input":
176
+ raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
177
+ elif key == "strict":
178
+ strict = val
179
+ elif key == "max_file_size":
180
+ raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
181
+ elif key == "data":
182
+ for subkey, subval in val.items():
183
+ if subkey == "from_flags":
184
+ from_flags = subval
185
+ elif subkey == "to_flags":
186
+ to_flags = subval
187
+ elif subkey == "from_options":
188
+ from_options = subval
189
+ elif subkey == "to_options":
190
+ to_options = subval
191
+ elif subkey == COORD_GEN_KEY:
192
+ coord_gen = subval
193
+ if COORD_GEN_QUAL_KEY in val:
194
+ coord_gen_qual = val[COORD_GEN_QUAL_KEY]
195
+ else:
196
+ coord_gen_qual = DEFAULT_COORD_GEN_QUAL
197
+ elif subkey == COORD_GEN_QUAL_KEY:
198
+ # Handled alongside COORD_GEN_KEY above
199
+ pass
200
+ else:
201
+ pytest.fail(f"The key 'data[\"{subkey}\"]' was passed to `conversion_kwargs` but could not be "
202
+ "interpreted")
203
+ else:
204
+ pytest.fail(f"The key '{key}' was passed to `conversion_kwargs` but could not be interpreted")
205
+
206
+ # Cleanup of arguments
207
+ if from_format.startswith("."):
208
+ from_format = from_format[1:]
209
+
210
+ # Set up the input file where we expect it to be
211
+ source_input_file = os.path.realpath(os.path.join(get_input_test_data_loc(), test_spec.filename))
212
+ input_file = os.path.join(input_dir, test_spec.filename)
213
+ if (os.path.isfile(input_file)):
214
+ os.unlink(input_file)
215
+ os.symlink(source_input_file, input_file)
216
+
217
+ # Remove test files from Downloads directory if they exist.
218
+
219
+ log_file = os.path.realpath(os.path.join(os.path.expanduser("~/Downloads"), test_spec.log_filename))
220
+ if (os.path.isfile(log_file)):
221
+ os.remove(log_file)
222
+
223
+ output_file = os.path.realpath(os.path.join(os.path.expanduser("~/Downloads"), test_spec.out_filename))
224
+ if (os.path.isfile(output_file)):
225
+ os.remove(output_file)
226
+
227
+ # Get the homepage
228
+ driver.get(f"{origin}/")
229
+
230
+ wait_for_element(driver, "//select[@id='fromList']/option")
231
+
232
+ # Select from_format from the 'from' list.
233
+ driver.find_element(By.XPATH, f"//select[@id='fromList']/option[starts-with(.,'{from_format}:')]").click()
234
+
235
+ # Select to_format from the 'to' list.
236
+ driver.find_element(By.XPATH, f"//select[@id='toList']/option[starts-with(.,'{test_spec.to_format}:')]").click()
237
+
238
+ # Select converter from the available conversion options list.
239
+ driver.find_element(By.XPATH, f"//select[@id='success']/option[contains(.,'{test_spec.converter_name}')]").click()
240
+
241
+ # Click on the "Yes" button to accept the converter and go to the conversion page
242
+ driver.find_element(By.XPATH, "//input[@id='yesButton']").click()
243
+
244
+ # Request non-strict filename checking if desired
245
+ if not strict:
246
+ wait_and_find_element(driver, "//input[@id='extCheck']").click()
247
+
248
+ # Select the input file
249
+ wait_and_find_element(driver, "//input[@id='fileToUpload']").send_keys(str(input_file))
250
+
251
+ # An alert may be present here, which we check for using a try block
252
+ try:
253
+ WebDriverWait(driver, 0.2).until(EC.alert_is_present())
254
+ alert = Alert(driver)
255
+ alert_text = alert.text
256
+ alert.dismiss()
257
+ raise FileConverterInputException(alert_text)
258
+ except TimeoutException:
259
+ pass
260
+
261
+ # Request the log file
262
+ wait_and_find_element(driver, "//input[@id='requestLog']").click()
263
+
264
+ # Select appropriate format args. The args only have a text attribute, so we need to find the one that starts with
265
+ # each flag - since we don't have too many, iterating over all possible combinations is the easiest way
266
+ for (l_flags, select_id) in ((from_flags, "inFlags"),
267
+ (to_flags, "outFlags")):
268
+ if not l_flags:
269
+ continue
270
+ flags_select = Select(wait_and_find_element(driver, f"//select[@id='{select_id}']"))
271
+ for flag in l_flags:
272
+ found = False
273
+ for option in flags_select.options:
274
+ if option.text.startswith(f"{flag}:"):
275
+ flags_select.select_by_visible_text(option.text)
276
+ found = True
277
+ break
278
+ if not found:
279
+ raise ValueError(f"Flag {flag} was not found in {select_id} selection box for conversion from "
280
+ f"{from_format} to {test_spec.to_format} with converter {test_spec.converter_name}")
281
+
282
+ for (options_string, table_id) in ((from_options, "in_argFlags"),
283
+ (to_options, "out_argFlags")):
284
+ if not options_string:
285
+ continue
286
+
287
+ # Split each option into words, of which the first letter of each is the key and the remainder is the value
288
+ l_options = options_string.split()
289
+
290
+ # Get the rows in the options table
291
+ options_table = wait_and_find_element(driver, f"//table[@id='{table_id}']")
292
+ l_rows = options_table.find_elements(By.XPATH, "./tr")
293
+
294
+ # Look for and set each option
295
+ for option in l_options:
296
+ found = False
297
+ for row in l_rows:
298
+ l_items = row.find_elements(By.XPATH, "./td")
299
+ label = l_items[1]
300
+ if not label.text.startswith(option[0]):
301
+ continue
302
+
303
+ # Select the option by clicking the box at the first element in the row to make the input appear
304
+ l_items[0].click()
305
+
306
+ # Input the option in the input box that appears in the third position in the row
307
+ input_box = wait_and_find_element(l_items[2], "./input")
308
+ input_box.send_keys(option[1:])
309
+
310
+ found = True
311
+ break
312
+
313
+ if not found:
314
+ raise ValueError(f"Option {option} was not found in {table_id} options table for conversion from "
315
+ f"{from_format} to {test_spec.to_format} with converter {test_spec.converter_name}")
316
+
317
+ # If radio-button settings are supplied, apply them now
318
+ for setting, name, l_allowed in ((coord_gen, "coord_gen", L_ALLOWED_COORD_GENS),
319
+ (coord_gen_qual, "coord_gen_qual", L_ALLOWED_COORD_GEN_QUALS)):
320
+ if not setting:
321
+ continue
322
+
323
+ if setting not in l_allowed:
324
+ raise ValueError(f"Invalid {name} value supplied: {setting}. Allowed values are: " +
325
+ str(l_allowed))
326
+
327
+ setting_radio = wait_and_find_element(driver, f"//input[@value='{setting}']")
328
+ setting_radio.click()
329
+
330
+ # Click on the "Convert" button.
331
+ wait_and_find_element(driver, "//input[@id='uploadButton']").click()
332
+
333
+ # Handle alert box.
334
+ WebDriverWait(driver, TIMEOUT).until(EC.alert_is_present())
335
+ alert = Alert(driver)
336
+ alert_text = alert.text
337
+ alert.dismiss()
338
+
339
+ if alert_text.startswith("ERROR:"):
340
+ # Raise an appropriate exception type depending on if it's a recognised input issue or not
341
+ if "unexpected exception" in alert_text:
342
+ raise FileConverterAbortException(STATUS_CODE_GENERAL, alert_text)
343
+ raise FileConverterInputException(alert_text)
344
+
345
+ # Wait until the log file exists, since it's downloaded second
346
+ time_elapsed = 0
347
+ while not os.path.isfile(log_file):
348
+ time.sleep(1)
349
+ time_elapsed += 1
350
+ if time_elapsed > TIMEOUT:
351
+ pytest.fail(f"Download of {output_file} and {log_file} timed out")
352
+
353
+ time.sleep(1)
354
+
355
+ if not os.path.isfile(output_file):
356
+ raise FileConverterAbortException("ERROR: No output file was produced. Log contents:\n" +
357
+ open(log_file, "r").read())
358
+
359
+ # Move the output file and log file to the expected locations
360
+ for qual_filename in output_file, log_file:
361
+ base_filename = os.path.split(qual_filename)[1]
362
+ target_filename = os.path.join(output_dir, base_filename)
363
+ if os.path.isfile(target_filename):
364
+ os.remove(target_filename)
365
+ if os.path.isfile(qual_filename):
366
+ shutil.move(qual_filename, target_filename)
@@ -19,19 +19,55 @@ 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
25
26
  from psdi_data_conversion.dist import LINUX_LABEL, get_dist
26
27
  from psdi_data_conversion.file_io import is_archive, split_archive_ext
27
28
  from psdi_data_conversion.main import main as data_convert_main
28
- from psdi_data_conversion.testing.constants import INPUT_TEST_DATA_LOC
29
+ from psdi_data_conversion.testing.constants import (INPUT_TEST_DATA_LOC_IN_PROJECT, OUTPUT_TEST_DATA_LOC_IN_PROJECT,
30
+ TEST_DATA_LOC_IN_PROJECT)
31
+
32
+
33
+ def get_path_in_project(filename):
34
+ """Get the realpath to a file contained within the project, given its project-relative path"""
35
+
36
+ old_cwd = os.getcwd()
37
+
38
+ try:
39
+ os.chdir(os.path.join(psdi_data_conversion.__path__[0], ".."))
40
+ realpath = os.path.realpath(filename)
41
+
42
+ finally:
43
+ # Change back to the previous directory
44
+ os.chdir(old_cwd)
45
+
46
+ return realpath
47
+
48
+
49
+ def get_test_data_loc():
50
+ """Get the realpath of the base directory containing all data for tests"""
51
+ return get_path_in_project(TEST_DATA_LOC_IN_PROJECT)
52
+
53
+
54
+ def get_input_test_data_loc():
55
+ """Get the realpath of the base directory containing input data for tests"""
56
+ return get_path_in_project(INPUT_TEST_DATA_LOC_IN_PROJECT)
57
+
58
+
59
+ def get_output_test_data_loc():
60
+ """Get the realpath of the base directory containing expected output data for tests"""
61
+ return get_path_in_project(OUTPUT_TEST_DATA_LOC_IN_PROJECT)
29
62
 
30
63
 
31
64
  @dataclass
32
65
  class ConversionTestInfo:
33
66
  """Information about a tested conversion."""
34
67
 
68
+ run_type: str
69
+ """One of "library", "cla", or "gui", describing which type of test run was performed"""
70
+
35
71
  test_spec: SingleConversionTestSpec
36
72
  """The specification of the test conversion which was run to produce this"""
37
73
 
@@ -50,20 +86,23 @@ class ConversionTestInfo:
50
86
  captured_stderr: str | None = None
51
87
  """Any output to stderr while the test was run"""
52
88
 
89
+ exc_info: pytest.ExceptionInfo | None = None
90
+ """If the test conversion raised an exception, that exception's info, otherwise None"""
91
+
53
92
  @property
54
93
  def qualified_in_filename(self):
55
94
  """Get the fully-qualified name of the input file"""
56
- return os.path.join(self.input_dir, self.test_spec.filename)
95
+ return os.path.realpath(os.path.join(self.input_dir, self.test_spec.filename))
57
96
 
58
97
  @property
59
98
  def qualified_out_filename(self):
60
99
  """Get the fully-qualified name of the output file"""
61
- return os.path.join(self.output_dir, self.test_spec.out_filename)
100
+ return os.path.realpath(os.path.join(self.output_dir, self.test_spec.out_filename))
62
101
 
63
102
  @property
64
103
  def qualified_log_filename(self):
65
104
  """Get the fully-qualified name of the log file"""
66
- return os.path.join(self.output_dir, self.test_spec.log_filename)
105
+ return os.path.realpath(os.path.join(self.output_dir, self.test_spec.log_filename))
67
106
 
68
107
  @property
69
108
  def qualified_global_log_filename(self):
@@ -71,28 +110,6 @@ class ConversionTestInfo:
71
110
  return self.test_spec.global_log_filename
72
111
 
73
112
 
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
113
  @dataclass
97
114
  class ConversionTestSpec:
98
115
  """Class providing a specification for a test file conversion.
@@ -121,6 +138,11 @@ class ConversionTestSpec:
121
138
  expect_success: bool | Iterable[bool] = True
122
139
  """Whether or not to expect the test to succeed"""
123
140
 
141
+ skip: bool | Iterable[bool] = False
142
+ """If set to true, this test will be skipped and not run. Can also be set individually for certain tests within an
143
+ array. This should typically only be used when debugging to skip working tests to more easily focus on non-working
144
+ tests"""
145
+
124
146
  callback: (Callable[[ConversionTestInfo], str] |
125
147
  Iterable[Callable[[ConversionTestInfo], str]] | None) = None
126
148
  """Function to be called after the conversion is performed to check in detail whether results are as expected. It
@@ -187,6 +209,9 @@ class ConversionTestSpec:
187
209
  if attr_name in l_single_val_attrs:
188
210
  setattr(self, attr_name, [getattr(self, attr_name)]*self._len)
189
211
 
212
+ # Check if all tests should be skipped
213
+ self.skip_all = all(self.skip)
214
+
190
215
  def __len__(self):
191
216
  """Get the length from the member - valid only after `__post_init__` has been called"""
192
217
  return self._len
@@ -221,6 +246,9 @@ class SingleConversionTestSpec:
221
246
  expect_success: bool = True
222
247
  """Whether or not to expect the test to succeed"""
223
248
 
249
+ skip: bool = False
250
+ """If set to True, this test will be skipped, always returning success"""
251
+
224
252
  callback: (Callable[[ConversionTestInfo], str] | None) = None
225
253
  """Function to be called after the conversion is performed to check in detail whether results are as expected. It
226
254
  should take as its only argument a `ConversionTestInfo` and return a string. The string should be empty if the check
@@ -258,9 +286,14 @@ def run_test_conversion_with_library(test_spec: ConversionTestSpec):
258
286
  with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
259
287
  # Iterate over the test spec to run each individual test it defines
260
288
  for single_test_spec in test_spec:
289
+ if single_test_spec.skip:
290
+ print(f"Skipping single test spec {single_test_spec}")
291
+ continue
292
+ print(f"Running single test spec: {single_test_spec}")
261
293
  _run_single_test_conversion_with_library(test_spec=single_test_spec,
262
294
  input_dir=input_dir,
263
295
  output_dir=output_dir)
296
+ print(f"Success for test spec: {single_test_spec}")
264
297
 
265
298
 
266
299
  def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec,
@@ -279,9 +312,9 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
279
312
  """
280
313
 
281
314
  # Symlink the input file to the input directory
282
- qualified_in_filename = os.path.join(input_dir, test_spec.filename)
315
+ qualified_in_filename = os.path.realpath(os.path.join(input_dir, test_spec.filename))
283
316
  try:
284
- os.symlink(os.path.join(INPUT_TEST_DATA_LOC, test_spec.filename),
317
+ os.symlink(os.path.join(get_input_test_data_loc(), test_spec.filename),
285
318
  qualified_in_filename)
286
319
  except FileExistsError:
287
320
  pass
@@ -316,13 +349,14 @@ def _run_single_test_conversion_with_library(test_spec: SingleConversionTestSpec
316
349
 
317
350
  # Compile output info for the test and call the callback function if one is provided
318
351
  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)
352
+ test_info = ConversionTestInfo(run_type="library",
353
+ test_spec=test_spec,
354
+ input_dir=input_dir,
355
+ output_dir=output_dir,
356
+ success=success,
357
+ captured_stdout=stdout,
358
+ captured_stderr=stderr,
359
+ exc_info=exc_info)
326
360
  callback_msg = test_spec.callback(test_info)
327
361
  assert not callback_msg, callback_msg
328
362
 
@@ -339,9 +373,14 @@ def run_test_conversion_with_cla(test_spec: ConversionTestSpec):
339
373
  with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
340
374
  # Iterate over the test spec to run each individual test it defines
341
375
  for single_test_spec in test_spec:
376
+ if single_test_spec.skip:
377
+ print(f"Skipping single test spec {single_test_spec}")
378
+ continue
379
+ print(f"Running single test spec: {single_test_spec}")
342
380
  _run_single_test_conversion_with_cla(test_spec=single_test_spec,
343
381
  input_dir=input_dir,
344
382
  output_dir=output_dir)
383
+ print(f"Success for test spec: {single_test_spec}")
345
384
 
346
385
 
347
386
  def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
@@ -360,9 +399,9 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
360
399
  """
361
400
 
362
401
  # Symlink the input file to the input directory
363
- qualified_in_filename = os.path.join(input_dir, test_spec.filename)
402
+ qualified_in_filename = os.path.realpath(os.path.join(input_dir, test_spec.filename))
364
403
  try:
365
- os.symlink(os.path.join(INPUT_TEST_DATA_LOC, test_spec.filename),
404
+ os.symlink(os.path.join(get_input_test_data_loc(), test_spec.filename),
366
405
  qualified_in_filename)
367
406
  except FileExistsError:
368
407
  pass
@@ -392,7 +431,7 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
392
431
  # Get the success from whether or not the exit code is 0
393
432
  success = not exc_info.value.code
394
433
 
395
- qualified_out_filename = os.path.join(output_dir, test_spec.out_filename)
434
+ qualified_out_filename = os.path.realpath(os.path.join(output_dir, test_spec.out_filename))
396
435
 
397
436
  # Determine success based on whether or not the output file exists with non-zero size
398
437
  if not os.path.isfile(qualified_out_filename) or os.path.getsize(qualified_out_filename) == 0:
@@ -405,12 +444,13 @@ def _run_single_test_conversion_with_cla(test_spec: SingleConversionTestSpec,
405
444
 
406
445
  # Compile output info for the test and call the callback function if one is provided
407
446
  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)
447
+ test_info = ConversionTestInfo(run_type="cla",
448
+ test_spec=test_spec,
449
+ input_dir=input_dir,
450
+ output_dir=output_dir,
451
+ success=success,
452
+ captured_stdout=stdout,
453
+ captured_stderr=stderr)
414
454
  callback_msg = test_spec.callback(test_info)
415
455
  assert not callback_msg, callback_msg
416
456
 
@@ -441,6 +481,8 @@ def run_converter_through_cla(filename: str,
441
481
  The directory which contains the output file
442
482
  log_file : str
443
483
  The desired name of the log file
484
+ conversion_kwargs : Any
485
+ Additional arguments describing the conversion
444
486
  """
445
487
 
446
488
  # Start the argument string with the arguments we will always include