psdi-data-conversion 0.0.37__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.
- psdi_data_conversion/app.py +30 -4
- psdi_data_conversion/constants.py +6 -5
- psdi_data_conversion/converter.py +13 -6
- psdi_data_conversion/converters/base.py +56 -53
- psdi_data_conversion/converters/c2x.py +1 -0
- psdi_data_conversion/converters/openbabel.py +10 -10
- psdi_data_conversion/database.py +335 -113
- psdi_data_conversion/main.py +151 -69
- psdi_data_conversion/static/javascript/data.js +18 -4
- psdi_data_conversion/static/javascript/format.js +22 -9
- psdi_data_conversion/templates/index.htm +57 -48
- psdi_data_conversion/testing/constants.py +3 -0
- psdi_data_conversion/testing/conversion_callbacks.py +2 -1
- psdi_data_conversion/testing/conversion_test_specs.py +27 -15
- psdi_data_conversion/testing/gui.py +354 -292
- psdi_data_conversion/testing/utils.py +35 -16
- {psdi_data_conversion-0.0.37.dist-info → psdi_data_conversion-0.0.38.dist-info}/METADATA +88 -4
- {psdi_data_conversion-0.0.37.dist-info → psdi_data_conversion-0.0.38.dist-info}/RECORD +21 -21
- {psdi_data_conversion-0.0.37.dist-info → psdi_data_conversion-0.0.38.dist-info}/WHEEL +0 -0
- {psdi_data_conversion-0.0.37.dist-info → psdi_data_conversion-0.0.38.dist-info}/entry_points.txt +0 -0
- {psdi_data_conversion-0.0.37.dist-info → psdi_data_conversion-0.0.38.dist-info}/licenses/LICENSE +0 -0
@@ -4,12 +4,12 @@
|
|
4
4
|
Utilities to aid in testing of the GUI
|
5
5
|
"""
|
6
6
|
|
7
|
-
|
8
7
|
import os
|
9
8
|
import shutil
|
10
9
|
from tempfile import TemporaryDirectory
|
11
10
|
|
12
11
|
import time
|
12
|
+
from dataclasses import dataclass
|
13
13
|
import pytest
|
14
14
|
from selenium.common.exceptions import TimeoutException
|
15
15
|
from selenium.webdriver.common.alert import Alert
|
@@ -24,7 +24,9 @@ from psdi_data_conversion.converters.base import (FileConverterAbortException, F
|
|
24
24
|
FileConverterInputException)
|
25
25
|
from psdi_data_conversion.converters.openbabel import (COORD_GEN_KEY, COORD_GEN_QUAL_KEY, DEFAULT_COORD_GEN_QUAL,
|
26
26
|
L_ALLOWED_COORD_GEN_QUALS, L_ALLOWED_COORD_GENS)
|
27
|
+
from psdi_data_conversion.database import get_format_info
|
27
28
|
from psdi_data_conversion.file_io import split_archive_ext
|
29
|
+
from psdi_data_conversion.testing.constants import DEFAULT_ORIGIN
|
28
30
|
from psdi_data_conversion.testing.utils import (ConversionTestInfo, ConversionTestSpec, SingleConversionTestSpec,
|
29
31
|
get_input_test_data_loc)
|
30
32
|
|
@@ -43,324 +45,384 @@ def wait_and_find_element(root: WebDriver | EC.WebElement, xpath: str, by=By.XPA
|
|
43
45
|
return root.find_element(by, xpath)
|
44
46
|
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
48
|
+
@dataclass
|
49
|
+
class GuiTestSpecRunner():
|
50
|
+
"""Class which provides an interface to run test conversions through the GUI
|
96
51
|
"""
|
97
52
|
|
98
|
-
|
99
|
-
|
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
|
-
"""
|
53
|
+
driver: WebDriver
|
54
|
+
"""The WebDriver to be used for testing"""
|
154
55
|
|
155
|
-
|
156
|
-
|
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")
|
56
|
+
origin: str = DEFAULT_ORIGIN
|
57
|
+
"""The address of the homepage of the testing server"""
|
205
58
|
|
206
|
-
|
207
|
-
|
208
|
-
from_format = from_format[1:]
|
59
|
+
def run(self, test_spec: ConversionTestSpec):
|
60
|
+
"""Run the test conversions outlined in a test spec"""
|
209
61
|
|
210
|
-
|
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)
|
62
|
+
self._test_spec = test_spec
|
216
63
|
|
217
|
-
|
64
|
+
# Make temporary directories for the input and output files to be stored in
|
65
|
+
with TemporaryDirectory("_input") as input_dir, TemporaryDirectory("_output") as output_dir:
|
218
66
|
|
219
|
-
|
220
|
-
|
221
|
-
|
67
|
+
# Iterate over the test spec to run each individual test it defines
|
68
|
+
for single_test_spec in test_spec:
|
69
|
+
if single_test_spec.skip:
|
70
|
+
print(f"Skipping single test spec {single_test_spec}")
|
71
|
+
continue
|
222
72
|
|
223
|
-
|
224
|
-
if (os.path.isfile(output_file)):
|
225
|
-
os.remove(output_file)
|
73
|
+
print(f"Running single test spec: {single_test_spec}")
|
226
74
|
|
227
|
-
|
228
|
-
|
75
|
+
GuiSingleTestSpecRunner(parent=self,
|
76
|
+
input_dir=input_dir,
|
77
|
+
output_dir=output_dir,
|
78
|
+
single_test_spec=single_test_spec).run()
|
229
79
|
|
230
|
-
|
80
|
+
print(f"Success for test spec: {single_test_spec}")
|
231
81
|
|
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
82
|
|
235
|
-
|
236
|
-
|
83
|
+
class GuiSingleTestSpecRunner:
|
84
|
+
"""Class which handles running an individual test conversion
|
85
|
+
"""
|
237
86
|
|
238
|
-
|
239
|
-
|
87
|
+
def __init__(self,
|
88
|
+
parent: GuiTestSpecRunner,
|
89
|
+
input_dir: str,
|
90
|
+
output_dir: str,
|
91
|
+
single_test_spec: SingleConversionTestSpec):
|
92
|
+
"""
|
93
|
+
|
94
|
+
Parameters
|
95
|
+
----------
|
96
|
+
parent : GuiTestSpecRunner
|
97
|
+
The GuiTestSpecRunner which created this and is running it
|
98
|
+
input_dir : str
|
99
|
+
The temporary directory to be used for input data
|
100
|
+
output_dir : str
|
101
|
+
The temporary directory to be used for output data
|
102
|
+
single_test_spec : SingleConversionTestSpec
|
103
|
+
The test spec that is currently being tested
|
104
|
+
"""
|
105
|
+
|
106
|
+
self.input_dir: str = input_dir
|
107
|
+
self.output_dir: str = output_dir
|
108
|
+
self.single_test_spec: SingleConversionTestSpec = single_test_spec
|
109
|
+
|
110
|
+
# Inherit data from the parent class
|
111
|
+
|
112
|
+
self.driver: WebDriver = parent.driver
|
113
|
+
"""The WebDriver to be used for testing"""
|
114
|
+
|
115
|
+
self.origin: str = parent.origin
|
116
|
+
"""The address of the homepage of the testing server"""
|
117
|
+
|
118
|
+
# Interpret information from the test spec that we'll need for testing
|
119
|
+
|
120
|
+
# Get just the local filename
|
121
|
+
self._filename = os.path.split(self.single_test_spec.filename)[1]
|
122
|
+
|
123
|
+
# Default options for conversion
|
124
|
+
self._base_filename, ext = split_archive_ext(self._filename)
|
125
|
+
self._strict = True
|
126
|
+
self._from_flags: str | None = None
|
127
|
+
self._to_flags: str | None = None
|
128
|
+
self._from_options: str | None = None
|
129
|
+
self._to_options: str | None = None
|
130
|
+
self._coord_gen = None
|
131
|
+
self._coord_gen_qual = None
|
132
|
+
|
133
|
+
# Get the from_format from the extension if not provided
|
134
|
+
from_format = single_test_spec.from_format
|
135
|
+
if not from_format:
|
136
|
+
from_format = ext
|
137
|
+
|
138
|
+
# Get the format info for each format, which we'll use to get the name and note of each
|
139
|
+
self._from_format_info = get_format_info(from_format, which=0)
|
140
|
+
self._to_format_info = get_format_info(single_test_spec.to_format, which=0)
|
141
|
+
|
142
|
+
# For each argument in the conversion kwargs, interpret it as the appropriate option for this conversion,
|
143
|
+
# overriding defaults set above
|
144
|
+
for key, val in self.single_test_spec.conversion_kwargs.items():
|
145
|
+
if key == "log_mode":
|
146
|
+
raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
|
147
|
+
elif key == "delete_input":
|
148
|
+
raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
|
149
|
+
elif key == "strict":
|
150
|
+
self._strict = val
|
151
|
+
elif key == "max_file_size":
|
152
|
+
raise ValueError(f"The conversion kwarg {key} is not valid with conversions through the GUI")
|
153
|
+
elif key == "data":
|
154
|
+
for subkey, subval in val.items():
|
155
|
+
if subkey == "from_flags":
|
156
|
+
self._from_flags = subval
|
157
|
+
elif subkey == "to_flags":
|
158
|
+
self._to_flags = subval
|
159
|
+
elif subkey == "from_options":
|
160
|
+
self._from_options = subval
|
161
|
+
elif subkey == "to_options":
|
162
|
+
self._to_options = subval
|
163
|
+
elif subkey == COORD_GEN_KEY:
|
164
|
+
self._coord_gen = subval
|
165
|
+
if COORD_GEN_QUAL_KEY in val:
|
166
|
+
self._coord_gen_qual = val[COORD_GEN_QUAL_KEY]
|
167
|
+
else:
|
168
|
+
self._coord_gen_qual = DEFAULT_COORD_GEN_QUAL
|
169
|
+
elif subkey == COORD_GEN_QUAL_KEY:
|
170
|
+
# Handled alongside COORD_GEN_KEY above
|
171
|
+
pass
|
172
|
+
else:
|
173
|
+
pytest.fail(f"The key 'data[\"{subkey}\"]' was passed to `conversion_kwargs` but could not be "
|
174
|
+
"interpreted")
|
175
|
+
else:
|
176
|
+
pytest.fail(f"The key '{key}' was passed to `conversion_kwargs` but could not be interpreted")
|
177
|
+
|
178
|
+
def run(self):
|
179
|
+
"""Run the conversion outlined in the test spec"""
|
180
|
+
|
181
|
+
exc_info: pytest.ExceptionInfo | None = None
|
182
|
+
if self.single_test_spec.expect_success:
|
183
|
+
try:
|
184
|
+
self._run_conversion()
|
185
|
+
success = False
|
186
|
+
except Exception:
|
187
|
+
print(f"Unexpected exception raised for single test spec {self.single_test_spec}")
|
188
|
+
raise
|
189
|
+
else:
|
190
|
+
with pytest.raises(FileConverterException) as exc_info:
|
191
|
+
self._run_conversion()
|
192
|
+
success = False
|
193
|
+
|
194
|
+
# Compile output info for the test and call the callback function if one is provided
|
195
|
+
if self.single_test_spec.callback:
|
196
|
+
test_info = ConversionTestInfo(run_type="gui",
|
197
|
+
test_spec=self.single_test_spec,
|
198
|
+
input_dir=self.input_dir,
|
199
|
+
output_dir=self.output_dir,
|
200
|
+
success=success,
|
201
|
+
exc_info=exc_info)
|
202
|
+
callback_msg = self.single_test_spec.callback(test_info)
|
203
|
+
if callback_msg:
|
204
|
+
pytest.fail(callback_msg)
|
205
|
+
|
206
|
+
def _run_conversion(self):
|
207
|
+
"""Run a conversion through the GUI
|
208
|
+
"""
|
209
|
+
|
210
|
+
self._set_up_files()
|
211
|
+
|
212
|
+
self._select_formats_and_converter()
|
213
|
+
|
214
|
+
self._set_conversion_settings()
|
215
|
+
|
216
|
+
self._provide_input_file()
|
217
|
+
|
218
|
+
self._request_conversion()
|
219
|
+
|
220
|
+
self._move_output()
|
221
|
+
|
222
|
+
def _set_up_files(self):
|
223
|
+
"""Set up the filenames we expect and initialize them - delete any leftover files and symlink the input file
|
224
|
+
to the desired location
|
225
|
+
"""
|
226
|
+
# Set up the expected filenames
|
227
|
+
source_input_file = os.path.realpath(os.path.join(get_input_test_data_loc(), self.single_test_spec.filename))
|
228
|
+
self._input_file = os.path.join(self.input_dir, self.single_test_spec.filename)
|
229
|
+
|
230
|
+
self._log_file = os.path.realpath(os.path.join(os.path.expanduser("~/Downloads"),
|
231
|
+
self.single_test_spec.log_filename))
|
232
|
+
|
233
|
+
self._output_file = os.path.realpath(os.path.join(os.path.expanduser("~/Downloads"),
|
234
|
+
self.single_test_spec.out_filename))
|
235
|
+
|
236
|
+
# Clean up any leftover files
|
237
|
+
if (os.path.isfile(self._input_file)):
|
238
|
+
os.unlink(self._input_file)
|
239
|
+
if (os.path.isfile(self._log_file)):
|
240
|
+
os.remove(self._log_file)
|
241
|
+
if (os.path.isfile(self._output_file)):
|
242
|
+
os.remove(self._output_file)
|
243
|
+
|
244
|
+
# Symlink the input file to the desired location
|
245
|
+
os.symlink(source_input_file, self._input_file)
|
246
|
+
|
247
|
+
def _select_formats_and_converter(self):
|
248
|
+
"""Handle the tasks on the format and converter selection page when running a test:
|
249
|
+
|
250
|
+
1. Load the main page (waiting for it to fully load)
|
251
|
+
2. Select the input and output formats
|
252
|
+
3. Select the converter
|
253
|
+
4. Click the "Yes" button to confirm and go to the convert page
|
254
|
+
"""
|
255
|
+
|
256
|
+
# Get the homepage
|
257
|
+
self.driver.get(f"{self.origin}/")
|
258
|
+
|
259
|
+
wait_for_element(self.driver, "//select[@id='fromList']/option")
|
260
|
+
|
261
|
+
# Select from_format from the 'from' list.
|
262
|
+
self.driver.find_element(
|
263
|
+
By.XPATH, f"//select[@id='fromList']/option[starts-with(.,'{self._from_format_info.name}:')]").click()
|
264
|
+
|
265
|
+
# Select to_format from the 'to' list.
|
266
|
+
self.driver.find_element(
|
267
|
+
By.XPATH, f"//select[@id='toList']/option[starts-with(.,'{self._to_format_info.name}:')]").click()
|
268
|
+
|
269
|
+
# Select converter from the available conversion options list.
|
270
|
+
self.driver.find_element(
|
271
|
+
By.XPATH, f"//select[@id='success']/option[contains(.,'{self.single_test_spec.converter_name}')]").click()
|
272
|
+
|
273
|
+
# Click on the "Yes" button to accept the converter and go to the conversion page
|
274
|
+
self.driver.find_element(By.XPATH, "//input[@id='yesButton']").click()
|
275
|
+
|
276
|
+
def _set_conversion_settings(self):
|
277
|
+
"""Set settings on the convert page appropriately for the desired conversion
|
278
|
+
"""
|
279
|
+
# Request non-strict filename checking if desired
|
280
|
+
if not self._strict:
|
281
|
+
wait_and_find_element(self.driver, "//input[@id='extCheck']").click()
|
282
|
+
|
283
|
+
# Request the log file
|
284
|
+
wait_and_find_element(self.driver, "//input[@id='requestLog']").click()
|
285
|
+
|
286
|
+
# Set appropriate format and converter settings for this conversion
|
287
|
+
self._select_format_flags()
|
288
|
+
self._set_format_options()
|
289
|
+
self._apply_radio_settings()
|
290
|
+
|
291
|
+
def _provide_input_file(self):
|
292
|
+
"""Provide the input file for the conversion, checking if any alert is raised in response
|
293
|
+
"""
|
294
|
+
# Select the input file
|
295
|
+
wait_and_find_element(self.driver, "//input[@id='fileToUpload']").send_keys(str(self._input_file))
|
240
296
|
|
241
|
-
|
242
|
-
|
297
|
+
# An alert may be present here, which we check for using a try block
|
298
|
+
try:
|
299
|
+
WebDriverWait(self.driver, 0.2).until(EC.alert_is_present())
|
300
|
+
alert = Alert(self.driver)
|
301
|
+
alert_text = alert.text
|
302
|
+
alert.dismiss()
|
303
|
+
raise FileConverterInputException(alert_text)
|
304
|
+
except TimeoutException:
|
305
|
+
pass
|
306
|
+
|
307
|
+
def _select_format_flags(self):
|
308
|
+
"""Select desired format flags. The options in the select box only have a text attribute, so we need to find
|
309
|
+
the one that starts with each flag - since we don't have too many, iterating over all possible combinations is
|
310
|
+
the easiest way
|
311
|
+
"""
|
312
|
+
for (l_flags, select_id) in ((self._from_flags, "inFlags"),
|
313
|
+
(self._to_flags, "outFlags")):
|
314
|
+
if not l_flags:
|
315
|
+
continue
|
316
|
+
flags_select = Select(wait_and_find_element(self.driver, f"//select[@id='{select_id}']"))
|
317
|
+
for flag in l_flags:
|
318
|
+
found = False
|
319
|
+
for option in flags_select.options:
|
320
|
+
if option.text.startswith(f"{flag}:"):
|
321
|
+
flags_select.select_by_visible_text(option.text)
|
322
|
+
found = True
|
323
|
+
break
|
324
|
+
if not found:
|
325
|
+
raise ValueError(f"Flag {flag} was not found in {select_id} selection box for conversion from "
|
326
|
+
f"{self._from_format_info.name} to {self._to_format_info.name} with "
|
327
|
+
f"converter {self.single_test_spec.converter_name}")
|
328
|
+
|
329
|
+
def _set_format_options(self):
|
330
|
+
"""Set desired format options
|
331
|
+
"""
|
332
|
+
for (options_string, table_id) in ((self._from_options, "in_argFlags"),
|
333
|
+
(self._to_options, "out_argFlags")):
|
334
|
+
if not options_string:
|
335
|
+
continue
|
243
336
|
|
244
|
-
|
245
|
-
|
246
|
-
wait_and_find_element(driver, "//input[@id='extCheck']").click()
|
337
|
+
# Split each option into words, of which the first letter of each is the key and the remainder is the value
|
338
|
+
l_options = options_string.split()
|
247
339
|
|
248
|
-
|
249
|
-
|
340
|
+
# Get the rows in the options table
|
341
|
+
options_table = wait_and_find_element(self.driver, f"//table[@id='{table_id}']")
|
342
|
+
l_rows = options_table.find_elements(By.XPATH, "./tr")
|
343
|
+
|
344
|
+
# Look for and set each option
|
345
|
+
for option in l_options:
|
346
|
+
found = False
|
347
|
+
for row in l_rows:
|
348
|
+
l_items = row.find_elements(By.XPATH, "./td")
|
349
|
+
label = l_items[1]
|
350
|
+
if not label.text.startswith(option[0]):
|
351
|
+
continue
|
352
|
+
|
353
|
+
# Select the option by clicking the box at the first element in the row to make the input appear
|
354
|
+
l_items[0].click()
|
355
|
+
|
356
|
+
# Input the option in the input box that appears in the third position in the row
|
357
|
+
input_box = wait_and_find_element(l_items[2], "./input")
|
358
|
+
input_box.send_keys(option[1:])
|
250
359
|
|
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
360
|
found = True
|
277
361
|
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
362
|
|
306
|
-
|
307
|
-
|
308
|
-
|
363
|
+
if not found:
|
364
|
+
raise ValueError(f"Option {option} was not found in {table_id} options table for conversion from "
|
365
|
+
f"{self._from_format_info.name} to {self._to_format_info.name} with "
|
366
|
+
f"converter {self.single_test_spec.converter_name}")
|
309
367
|
|
310
|
-
|
311
|
-
|
368
|
+
def _apply_radio_settings(self):
|
369
|
+
"""Apply any radio-button settings desired for this conversion by clicking the appropriate radio buttons
|
370
|
+
"""
|
312
371
|
|
313
|
-
|
314
|
-
|
315
|
-
|
372
|
+
for setting, name, l_allowed in ((self._coord_gen, "coord_gen", L_ALLOWED_COORD_GENS),
|
373
|
+
(self._coord_gen_qual, "coord_gen_qual", L_ALLOWED_COORD_GEN_QUALS)):
|
374
|
+
if not setting:
|
375
|
+
continue
|
316
376
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
if not setting:
|
321
|
-
continue
|
377
|
+
if setting not in l_allowed:
|
378
|
+
raise ValueError(f"Invalid {name} value supplied: {setting}. Allowed values are: " +
|
379
|
+
str(l_allowed))
|
322
380
|
|
323
|
-
|
324
|
-
|
325
|
-
str(l_allowed))
|
381
|
+
setting_radio = wait_and_find_element(self.driver, f"//input[@value='{setting}']")
|
382
|
+
setting_radio.click()
|
326
383
|
|
327
|
-
|
328
|
-
|
384
|
+
def _request_conversion(self):
|
385
|
+
"""Request the conversion, handle the alert box that appears, and wait for the files to be downloaded
|
386
|
+
"""
|
387
|
+
# Click on the "Convert" button.
|
388
|
+
wait_and_find_element(self.driver, "//input[@id='uploadButton']").click()
|
329
389
|
|
330
|
-
|
331
|
-
|
390
|
+
# Handle alert box.
|
391
|
+
WebDriverWait(self.driver, TIMEOUT).until(EC.alert_is_present())
|
392
|
+
alert = Alert(self.driver)
|
393
|
+
alert_text = alert.text
|
394
|
+
alert.dismiss()
|
332
395
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
396
|
+
if alert_text.startswith("ERROR:"):
|
397
|
+
# Raise an appropriate exception type depending on if it's a recognised input issue or not
|
398
|
+
if "unexpected exception" in alert_text:
|
399
|
+
raise FileConverterAbortException(STATUS_CODE_GENERAL, alert_text)
|
400
|
+
raise FileConverterInputException(alert_text)
|
338
401
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
402
|
+
# Wait until the log file exists, since it's downloaded second
|
403
|
+
time_elapsed = 0
|
404
|
+
while not os.path.isfile(self._log_file):
|
405
|
+
time.sleep(1)
|
406
|
+
time_elapsed += 1
|
407
|
+
if time_elapsed > TIMEOUT:
|
408
|
+
pytest.fail(f"Download of {self._output_file} and {self._log_file} timed out")
|
344
409
|
|
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
410
|
time.sleep(1)
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
411
|
+
|
412
|
+
def _move_output(self):
|
413
|
+
"""Move the created output files out of the default Downloads directory and into the desired output files
|
414
|
+
directory.
|
415
|
+
"""
|
416
|
+
# Check for the presence of the output file
|
417
|
+
if not os.path.isfile(self._output_file):
|
418
|
+
raise FileConverterAbortException("ERROR: No output file was produced. Log contents:\n" +
|
419
|
+
open(self._log_file, "r").read())
|
420
|
+
|
421
|
+
# Move the output file and log file to the expected locations
|
422
|
+
for qual_filename in self._output_file, self._log_file:
|
423
|
+
self._base_filename = os.path.split(qual_filename)[1]
|
424
|
+
target_filename = os.path.join(self.output_dir, self._base_filename)
|
425
|
+
if os.path.isfile(target_filename):
|
426
|
+
os.remove(target_filename)
|
427
|
+
if os.path.isfile(qual_filename):
|
428
|
+
shutil.move(qual_filename, target_filename)
|