Skip to content

Commit 3f65b23

Browse files
authored
Merge pull request #18 from BritishGeologicalSurvey/test-cli
Test CLI
2 parents 6a70a0b + 61d37e0 commit 3f65b23

File tree

6 files changed

+357
-236
lines changed

6 files changed

+357
-236
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ jobs:
1919

2020
- name: Install dependencies
2121
run: |
22-
pip install -r requirements.txt
23-
pip install pytest
22+
python -m pip install -e .[dev]
2423
2524
- name: Run tests
2625
run: python -m pytest -vs

pyvolcans/pyvolcans.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ def cli():
8383
volcano_input = int(args.volcano)
8484
except ValueError:
8585
volcano_input = args.volcano
86-
except PyvolcansError as exc:
87-
# print error message and quit program on error
88-
logging.error(exc.args[0])
89-
sys.exit(1)
9086

9187
if args.verbose:
9288
logging.getLogger().setLevel(logging.DEBUG)

pyvolcans/pyvolcans_func.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,10 +591,13 @@ def output_many_volcanoes_data(top_analogues_list, filename):
591591
the ID profiles (volcano data) for all the top analogue volcanoes.
592592
"""
593593

594+
tmp_result_list = []
595+
594596
with open(filename, "w") as outfile:
595597
for volcano in top_analogues_list:
596598
result_dict = get_volcano_source_data(volcano)
597-
json.dump(result_dict, outfile, indent=2, sort_keys=False)
599+
tmp_result_list.append(result_dict)
600+
json.dump(tmp_result_list, outfile, indent=2, sort_keys=False)
598601

599602

600603
def get_analogies(my_volcano, volcans_result, count=10):

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ thefuzz
99
flake8
1010
ipdb
1111
ipython
12-
pylint
1312
pytest
13+
pytest-cov
1414
versioneer

test/test_pyvolcans.py

Lines changed: 86 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -1,229 +1,90 @@
1-
# -*- coding: utf-8 -*-
2-
"""
3-
Created on Fri May 15 12:49:55 2020
4-
5-
@author: Vyron Christodoulou, John A. Stevenson, Pablo Tierz
6-
(British Geological Survey, The Lyell Centre,
7-
Edinburgh, UK).
8-
"""
1+
import os
2+
import subprocess
93
import pytest
10-
11-
import numpy as np
4+
import json
125
import pandas as pd
13-
from pandas.testing import assert_frame_equal
14-
from pyvolcans.pyvolcans_func import (
15-
fuzzy_matching,
16-
match_name,
17-
format_volcano_name,
18-
get_volcano_idx_from_name,
19-
get_volcano_name_from_idx,
20-
get_volcano_number_from_name,
21-
get_volcano_idx_from_number,
22-
get_many_analogy_percentiles,
23-
calculate_weighted_analogy_matrix,
24-
open_gvp_website,
25-
plot_bar_apriori_analogues,
26-
plot_bar_better_analogues,
27-
set_weights_from_args,
28-
VOLCANO_NAMES,
29-
PyvolcansError
30-
)
31-
32-
# pylint: disable=missing-docstring
33-
34-
35-
def test_volcano_idx():
36-
idx = get_volcano_idx_from_name(volcano_name='Fuego')
37-
assert idx == 1071
38-
39-
40-
def test_volcano_name():
41-
name = get_volcano_name_from_idx(volcano_idx=1071)
42-
assert name == 'Fuego'
43-
44-
45-
def test_fuzzy_matching():
46-
volc_matches = fuzzy_matching('West Eifel')
47-
assert 'West Eifel Volcanic Field' in volc_matches
48-
49-
# Test with limit
50-
volc_matches_limit = fuzzy_matching('West Eiffel', limit=3)
51-
assert 'West Eifel Volcanic Field' in volc_matches_limit
52-
53-
54-
def test_volcano_number():
55-
number = get_volcano_number_from_name('Santorini')
56-
assert number == 212040
57-
58-
59-
def test_format_volcano_name():
60-
name = format_volcano_name('Tolima, Nevado del')
61-
assert name == 'Tolima_Nevado_del'
62-
63-
64-
def test_set_weights_from_args_error():
65-
args_dict = {'tectonic_setting': 99.00001,
66-
'geochemistry': 99.00001,
67-
'morphology': 99.00001,
68-
'eruption_size': 99.00001,
69-
'eruption_style': 99.00001}
70-
71-
with pytest.raises(PyvolcansError) as exc_info:
72-
set_weights_from_args(args_dict)
73-
74-
msg = ("Sum of weights (495.000050000) is different from 1!"
75-
" Please revise your weighting scheme.")
76-
77-
assert str(exc_info.value) == msg
78-
79-
80-
@pytest.mark.parametrize("args_dict, expected_weights", [
81-
({'tectonic_setting': 0.2,
82-
'morphology': 0.2,
83-
'eruption_size': 0.2,
84-
'geochemistry': 0.2,
85-
'eruption_style': 0.2}, [0.2, 0.2, 0.2, 0.2, 0.2]),
86-
({'tectonic_setting': None,
87-
'morphology': None,
88-
'eruption_size': None,
89-
'geochemistry': None,
90-
'eruption_style': None}, [0.2, 0.2, 0.2, 0.2, 0.2]),
91-
({'tectonic_setting': 1,
92-
'morphology': None,
93-
'eruption_size': None,
94-
'geochemistry': None,
95-
'eruption_style': None}, [1, 0, 0, 0, 0]),
96-
({'tectonic_setting': 0.2,
97-
'morphology': 0.4,
98-
'eruption_size': None,
99-
'geochemistry': 0.2,
100-
'eruption_style': 0.2}, [0.2, 0.4, 0, 0.2, 0.2])
6+
from pathlib import Path
7+
8+
9+
@pytest.mark.parametrize("input_args,expected",
10+
[("--help", "usage: pyvolcans"),
11+
("-h", "usage: pyvolcans"),
12+
# Odd behaviour with --verbose, half of the output is written to stderr and half to stdout
13+
("Hekla --verbose", "ID profile for Hekla, Iceland (372070):"),
14+
("Hekla -v", "ID profile for Hekla, Iceland (372070):"),
15+
("Hekla --apriori Santorini", "According to PyVOLCANS"),
16+
# Confirm warning raised for too many perfect analogues
17+
("Hekla -Ts 1", "UserWarning (PyVOLCANS): All top analogue volcanoes have the same value of total analogy"),
18+
# Confirm command line weightings are passed through correctly
19+
("Vesuvius -Ts 1 -v", "'tectonic_setting': 1.0"),
20+
("Vesuvius -G 1 -v", "'geochemistry': 1.0"),
21+
("Vesuvius -M 1 -v", "'morphology': 1.0"),
22+
("Vesuvius -Sz 1 -v", "'eruption_size': 1.0"),
23+
("Vesuvius -St 1 -v", "'eruption_style': 1.0"),
24+
("Fuego --count 50", "Top 50")])
25+
def test_pyvolcans_output(input_args, expected, capfd):
26+
subprocess.run(['pyvolcans', *input_args.split()])
27+
out, err = capfd.readouterr()
28+
assert (expected in out or expected in err)
29+
30+
31+
@pytest.mark.skipif(os.getenv('GITHUB_ACTIONS', 'false') == 'true',
32+
reason="Version number calculation doesn't work in GitHub Actions")
33+
@pytest.mark.parametrize("input_args,expected", [
34+
("--version", "v"),
35+
("-V", "v")
10136
])
102-
def test_set_weights_from_args(args_dict, expected_weights):
103-
names = list(args_dict.keys())
104-
result = set_weights_from_args(args_dict)
105-
106-
assert list(result.keys()) == names
107-
assert list(result.values()) == expected_weights
108-
109-
110-
def test_volcano_idx_from_number():
111-
idx = get_volcano_idx_from_number(212040)
112-
assert idx == 21
113-
114-
115-
@pytest.mark.parametrize("name,expected", [('blah', 'not found'), ('Santa Isabel', 'not unique')])
116-
def test_match_name(name, expected):
117-
with pytest.raises(PyvolcansError) as excinfo:
118-
match_name(name)
119-
assert expected in str(excinfo.value)
120-
121-
122-
@pytest.fixture
123-
def mock_analogies():
124-
"""
125-
Create mocked analogy matrices for each volcanological criterion.
126-
Note: The matrix is only one item long so tests must use 'West Eifel
127-
Volcanic Field' because it is first on the list.
128-
"""
129-
mock_analogies = {'tectonic_setting': np.array([40000]),
130-
'geochemistry': np.array([4000]),
131-
'morphology': np.array([400]),
132-
'eruption_size': np.array([40]),
133-
'eruption_style': np.array([4])}
134-
return mock_analogies
135-
136-
@pytest.fixture
137-
def mock_weights():
138-
mock_weights = {'tectonic_setting': 0.2,
139-
'geochemistry': 0.2,
140-
'morphology': 0.2,
141-
'eruption_size': 0.2,
142-
'eruption_style': 0.2}
143-
return mock_weights
144-
145-
146-
def test_plot_bar_apriori_analogues(mock_weights, mock_analogies):
147-
pandas_df = calculate_weighted_analogy_matrix('West Eifel Volcanic Field',
148-
mock_weights,
149-
mock_analogies)
150-
df_bar = plot_bar_apriori_analogues('West Eifel Volcanic Field',
151-
210010,
152-
['Hekla'],
153-
pandas_df,
154-
'Test_string')
155-
df_expected = pd.DataFrame({'name': ['Hekla'], 'ATs': [8000.0],
156-
'AG': [800.0], 'AM': [80.0], 'ASz':[8.0],
157-
'ASt': [0.8]}, index=[1362])
158-
assert_frame_equal(df_bar, df_expected)
159-
160-
161-
def test_plot_bar_better_analogues(mock_weights, mock_analogies):
162-
pandas_df = calculate_weighted_analogy_matrix('West Eifel Volcanic Field',
163-
mock_weights,
164-
mock_analogies)
165-
_, better_analogues = \
166-
get_many_analogy_percentiles('West Eifel Volcanic Field', ['Hekla'],
167-
pandas_df)
168-
169-
df_bar = plot_bar_better_analogues('West Eifel Volcanic Field',
170-
210010,
171-
better_analogues,
172-
'Test_string')
173-
174-
df_expected = pd.DataFrame({'apriori_analogue':['Hekla'],
175-
'percentage_better': [100]})
176-
assert_frame_equal(df_bar, df_expected)
177-
178-
179-
@pytest.mark.parametrize("weights, expected", [
180-
({'tectonic_setting': 0,
181-
'geochemistry': 0.25,
182-
'morphology': 0.25,
183-
'eruption_size': 0.25,
184-
'eruption_style': 0.25}, 1111),
185-
({'tectonic_setting': 0.25,
186-
'geochemistry': 0,
187-
'morphology': 0.25,
188-
'eruption_size': 0.25,
189-
'eruption_style': 0.25}, 10111),
190-
({'tectonic_setting': 0.25,
191-
'geochemistry': 0.25,
192-
'morphology': 0,
193-
'eruption_size': 0.25,
194-
'eruption_style': 0.25}, 11011),
195-
({'tectonic_setting': 0.25,
196-
'geochemistry': 0.25,
197-
'morphology': 0.25,
198-
'eruption_size': 0,
199-
'eruption_style': 0.25}, 11101),
200-
({'tectonic_setting': 0.25,
201-
'geochemistry': 0.25,
202-
'morphology': 0.25,
203-
'eruption_size': 0.25,
204-
'eruption_style': 0}, 11110)])
205-
def test_combined_analogy_matrix_no_tectonic(weights, expected, mock_analogies):
206-
pandas_df = calculate_weighted_analogy_matrix(
207-
'West Eifel Volcanic Field', weights, mock_analogies)
208-
matrix = pandas_df.loc[get_volcano_idx_from_name(
209-
'West Eifel Volcanic Field'), 'total_analogy']
210-
assert matrix.astype(int) == expected
211-
212-
213-
def test_open_gvp_website(monkeypatch):
214-
# Arrange
215-
def always_false(my_web):
216-
# This function will replace webbrowser.open and always returns false
217-
# We have my_web as an argument so our function has the same inputs as
218-
# webbrowser.open
219-
return False
220-
221-
monkeypatch.setattr('pyvolcans.pyvolcans_func.webbrowser.open', always_false)
222-
223-
# Act
224-
with pytest.raises(PyvolcansError) as exc_info:
225-
open_gvp_website(123456)
226-
37+
def test_pyvolcans_version_number_reporting(input_args, expected, capfd):
38+
subprocess.run(['pyvolcans', *input_args.split()])
39+
out, err = capfd.readouterr()
40+
assert (expected in out or expected in err)
41+
42+
43+
@pytest.mark.parametrize("input_args,expected",
44+
[("Hekla -ovd", "Hekla"),
45+
("Hekla -oad", "Torfajokull"),
46+
("Fuego -ovd", "Fuego"),
47+
("Fuego -oad", "Klyuchevskoy"),
48+
("Sabancaya -oad", "Peuet Sague")])
49+
def test_write_json_files(input_args, expected, tmp_path):
50+
subprocess.run(['pyvolcans', '--verbose', *input_args.split()],
51+
cwd=tmp_path)
52+
fname = list(Path(tmp_path).glob("*.json"))[0]
53+
with open(fname) as f:
54+
data = json.load(f)
55+
#`-ovd` returns a dict, `-oad` returns a list of dict
56+
if type(data) == dict:
57+
assert data['name'] == expected
58+
elif type(data) == list:
59+
top1_analogue = data[0]
60+
assert top1_analogue['name'] == expected
61+
62+
63+
@pytest.mark.parametrize("input_args,expected",
64+
[("Alutu -w", "222040"),
65+
("Vulcano -w", "263310")])
66+
def test_write_csv_files(input_args, expected, tmp_path):
67+
# Arrange / Action
68+
subprocess.run(['pyvolcans', '--verbose', *input_args.split()],
69+
cwd=tmp_path)
70+
fname = list(Path(tmp_path).glob("*.csv"))[0]
71+
data = pd.read_csv(fname)
72+
top1_analogue = data.iloc[0, ]
22773
# Assert
228-
msg = "No suitable browser to open https://volcano.si.edu/volcano.cfm?vn=123456&vtab=GeneralInfo"
229-
assert str(exc_info.value) == msg
74+
assert top1_analogue['smithsonian_id'] == int(expected)
75+
76+
@pytest.mark.parametrize("input_args,expected",
77+
[("Fuego -G 99", "PyVOLCANS: Sum of weights"),
78+
("Fuego -Sz -1", "PyVOLCANS: Some criterion weights"),
79+
("Fuigu", "Fuigu not found!"),
80+
("342090111", "Volcano number does not exist"),
81+
("-iurwhfgl", "arguments are required: volcano"),
82+
("Fuego --apriori Tungurahua Villarrico",
83+
"Villarrico not found!"),
84+
("Fuego --apriori 352010 -897",
85+
"Volcano number does not exist")])
86+
def test_pyvolcans_errors(input_args, expected, capfd):
87+
subprocess.run(['pyvolcans', *input_args.split()])
88+
_, err = capfd.readouterr()
89+
90+
assert expected in err

0 commit comments

Comments
 (0)