Passed
Pull Request — develop (#57)
by Angeline
01:06
created

test_py_aacgmv2.TestGetAACGMCoordArr.setup()   A

Complexity

Conditions 1

Size

Total Lines 12
Code Lines 11

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 11
dl 12
loc 12
rs 9.85
c 0
b 0
f 0
cc 1
nop 1
1
# -*- coding: utf-8 -*-
2
from __future__ import division, absolute_import, unicode_literals
3
4
import datetime as dt
5
from io import StringIO
6
import logging
7
import numpy as np
8
import os
9
from sys import version_info
10
import pytest
11
import warnings
12
13
import aacgmv2
14
15
16
class TestConvertArray:
17
    def setup(self):
18
        self.out = None
19
        self.ref = None
20
        self.rtol = 1.0e-4
21
22
    def teardown(self):
23
        del self.out, self.ref, self.rtol
24
25
    def evaluate_output(self):
26
        """ Function used to evaluate convert_latlon_arr output"""
27
        if self.out is not None:
28
            np.testing.assert_equal(len(self.out), len(self.ref))
29
            for i, oo in enumerate(self.out):
30
                if not isinstance(oo, np.ndarray):
31
                    raise TypeError("output value is not a numpy array")
32
    
33
                np.testing.assert_equal(len(oo), len(self.ref[i]))
34
                np.testing.assert_allclose(oo, self.ref[i], rtol=self.rtol)
35
36
37
class TestConvertLatLon:
38
    def setup(self):
39
        """Runs before every method to create a clean testing setup"""
40
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
41
        self.ddate = dt.date(2015, 1, 1)
42
        self.in_args = [60, 0]
43
        self.out = None
44
        self.rtol = 1.0e-4
45
46
    def teardown(self):
47
        """Runs after every method to clean up previous testing"""
48
        del self.out, self.in_args, self.rtol, self.dtime, self.ddate
49
50
    @pytest.mark.parametrize('alt,method_code,ref',
51
                             [(300, 'TRACE', [58.2268, 81.1613, 1.0457]),
52
                              (3000.0, "G2A|BADIDEA", [64.3578, 83.2895,
53
                                                       1.4694]),
54
                              (7000.0, "G2A|TRACE|BADIDEA",
55
                               [69.3187, 85.0845, 2.0973])])
56
    def test_convert_latlon(self, alt, method_code, ref):
57
        """Test single value latlon conversion"""
58
        self.in_args.extend([alt, self.dtime, method_code])
59
        self.out = aacgmv2.convert_latlon(*self.in_args)
60
        np.testing.assert_allclose(self.out, ref, rtol=self.rtol)
61
62
    @pytest.mark.parametrize('lat,ref',
63
                             [(90.01, [83.927161, 170.1471396, 1.04481923]),
64
                              (-90.01, [-74.9814852, 17.990332, 1.044819236])])
65
    def test_convert_latlon_high_lat(self, lat, ref):
66
        """Test single latlon conversion with latitude just out of bounds"""
67
        self.in_args[0] = lat
68
        self.in_args.extend([300, self.dtime, 'G2A'])
69
        self.out = aacgmv2.convert_latlon(*self.in_args)
70
        np.testing.assert_allclose(self.out, ref, rtol=self.rtol)
71
72
    def test_convert_latlon_datetime_date(self):
73
        """Test single latlon conversion with date and datetime input"""
74
        self.in_args.extend([300, self.ddate, 'TRACE'])
75
        self.out = aacgmv2.convert_latlon(*self.in_args)
76
        np.testing.assert_allclose(self.out, [58.2268, 81.1613, 1.0457],
77
                                   rtol=self.rtol)
78
79
    @pytest.mark.skipif(version_info.major == 2,
80
                        reason='Not raised in Python 2')
81
    def test_convert_latlon_location_failure(self):
82
        """Test single value latlon conversion with a bad location"""
83
        self.out = aacgmv2.convert_latlon(0, 0, 0, self.dtime, self.in_args[-1])
84
        assert np.all(np.isnan(np.array(self.out)))
85
86
    def test_convert_latlon_maxalt_failure(self):
87
        """test convert_latlon failure for an altitude too high for coeffs"""
88
        self.in_args.extend([2001, self.dtime, ""])
89
        self.out = aacgmv2.convert_latlon(*self.in_args)
90
        assert np.all(np.isnan(np.array(self.out)))
91
92
    @pytest.mark.parametrize('in_rep,in_irep,msg',
93
                             [(None, 3, "must be a datetime object"),
94
                              (91, 0, "unrealistic latitude"),
95
                              (-91, 0, "unrealistic latitude"),
96
                              (None, 4, "unknown method code")])
97
    def test_convert_latlon_failure(self, in_rep, in_irep, msg):
98
        self.in_args.extend([300, self.dtime, "G2A"])
99
        self.in_args[in_irep] = in_rep
100
        with pytest.raises(ValueError, match=msg):
101
            aacgmv2.convert_latlon(*self.in_args)
102
103
104
class TestConvertLatLonArr(TestConvertArray):
105 View Code Duplication
    def setup(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
106
        """Runs before every method to create a clean testing setup"""
107
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
108
        self.ddate = dt.date(2015, 1, 1)
109
        self.lat_in = [60.0, 61.0]
110
        self.lon_in = [0.0, 0.0]
111
        self.alt_in = [300.0, 300.0]
112
        self.method = 'TRACE'
113
        self.out = None
114
        self.ref = [[58.2268, 59.3184], [81.1613, 81.6080], [1.0457, 1.0456]]
115
        self.rtol = 1.0e-4
116
117
    def teardown(self):
118
        """Runs after every method to clean up previous testing"""
119
        del self.lat_in, self.lon_in, self.alt_in, self.dtime, self.ddate
120
        del self.method, self.out, self.ref, self.rtol
121
122
    def test_convert_latlon_arr_single_val(self):
123
        """Test array latlon conversion for a single value"""
124
        self.out = aacgmv2.convert_latlon_arr(self.lat_in[0], self.lon_in[0],
125
                                              self.alt_in[0], self.dtime,
126
                                              self.method)
127
        self.ref = [[rr[0]] for rr in self.ref]
128
        self.evaluate_output()
129
130
    def test_convert_latlon_arr_arr_single(self):
131
        """Test array latlon conversion for array input of shape (1,)"""
132
        self.out = aacgmv2.convert_latlon_arr(np.array([self.lat_in[0]]),
133
                                              np.array([self.lon_in[0]]),
134
                                              np.array([self.alt_in[0]]),
135
                                              self.dtime, self.method)
136
        self.ref = [[rr[0]] for rr in self.ref]
137
        self.evaluate_output()
138
139
    def test_convert_latlon_arr_list_single(self):
140
        """Test array latlon conversion for list input of single values"""
141
        self.out = aacgmv2.convert_latlon_arr([self.lat_in[0]],
142
                                              [self.lon_in[0]],
143
                                              [self.alt_in[0]], self.dtime,
144
                                              self.method)
145
        self.ref = [[rr[0]] for rr in self.ref]
146
        self.evaluate_output()
147
148
    def test_convert_latlon_arr_list(self):
149
        """Test array latlon conversion for list input"""
150
        self.out = aacgmv2.convert_latlon_arr(self.lat_in, self.lon_in,
151
                                              self.alt_in, self.dtime,
152
                                              self.method)
153
        self.evaluate_output()
154
155
    def test_convert_latlon_arr_arr(self):
156
        """Test array latlon conversion for array input"""
157
        self.out = aacgmv2.convert_latlon_arr(np.array(self.lat_in),
158
                                              np.array(self.lon_in),
159
                                              np.array(self.alt_in),
160
                                              self.dtime, self.method)
161
        self.evaluate_output()
162
163
    def test_convert_latlon_arr_list_mix(self):
164
        """Test array latlon conversion for mixed types with list"""
165
        self.out = aacgmv2.convert_latlon_arr(self.lat_in, self.lon_in[0],
166
                                              self.alt_in[0], self.dtime,
167
                                              self.method)
168
        self.evaluate_output()
169
170
    def test_convert_latlon_arr_arr_mix(self):
171
        """Test array latlon conversion for mixed type with an array"""
172
        self.out = aacgmv2.convert_latlon_arr(np.array(self.lat_in),
173
                                              self.lon_in[0], self.alt_in[0],
174
                                              self.dtime, self.method)
175
        self.evaluate_output()
176
177
    def test_convert_latlon_arr_arr_mult_and_single_element(self):
178
        """Test latlon conversion for arrays with multiple and single vals"""
179
        self.out = aacgmv2.convert_latlon_arr(np.array(self.lat_in),
180
                                              np.array([self.lon_in[0]]),
181
                                              np.array(self.alt_in),
182
                                              self.dtime, self.method)
183
        self.evaluate_output()
184
185
    @pytest.mark.parametrize('method_code,alt,local_ref',
186
                             [("BADIDEA", 3000.0,
187
                               [[64.3580], [83.2895], [1.4694]]),
188
                              ("BADIDEA|TRACE", 7000.0,
189
                               [[69.3187], [85.0845], [2.0973]])])
190
    def test_convert_latlon_arr_badidea(self, method_code, alt, local_ref):
191
        """Test array latlon conversion for BADIDEA"""
192
        self.out = aacgmv2.convert_latlon_arr(self.lat_in[0], self.lon_in[0],
193
                                              [alt], self.dtime, method_code)
194
        self.ref = local_ref
195
        self.evaluate_output()
196
197
    @pytest.mark.skipif(version_info.major == 2,
198
                        reason='Not raised in Python 2')
199
    def test_convert_latlon_arr_location_failure(self):
200
        """Test array latlon conversion with a bad location"""
201
202
        with warnings.catch_warnings():
203
            # Causes all warnings to be surpressed
204
            warnings.simplefilter("ignore")
205
206
            # Trigger a warning
207
            self.out = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime, "")
208
209
            # Test the output
210
            np.testing.assert_equal(len(self.out), len(self.ref))
211
            assert np.any(~np.isfinite(np.array(self.out)))
212
213
    def test_convert_latlon_arr_datetime_date(self):
214
        """Test array latlon conversion with date and datetime input"""
215
        self.out = aacgmv2.convert_latlon_arr(self.lat_in, self.lon_in,
216
                                              self.alt_in, self.ddate,
217
                                              self.method)
218
        self.evaluate_output()
219
220
    def test_convert_latlon_arr_clip(self):
221
        """Test array latlon conversion with latitude clipping"""
222
        self.lat_in = [90.01, -90.01]
223
        self.ref = [[83.92352053, -74.98110552], [170.1381271, 17.98164313],
224
                    [1.04481924, 1.04481924]]
225
        self.out = aacgmv2.convert_latlon_arr(self.lat_in, self.lon_in,
226
                                              self.alt_in, self.ddate,
227
                                              self.method)
228
        self.evaluate_output()
229
230
    def test_convert_latlon_arr_maxalt_failure(self):
231
        """test convert_latlon_arr failure for altitudes too high for coeffs"""
232
        self.method = ""
233
        self.out = aacgmv2.convert_latlon_arr(self.lat_in[0], self.lon_in[0],
234
                                              [2001], self.dtime, self.method)
235
        assert np.all(np.isnan(np.array(self.out)))
236
237
    @pytest.mark.parametrize('in_rep,in_irep,msg',
238
                             [(None, 3, "must be a datetime object"),
239
                              ([np.full(shape=(3, 2), fill_value=50.0), 0],
240
                               [0, 1], "unable to process multi-dimensional"),
241
                              ([50, 60, 70], 0, "arrays are mismatched"),
242
                              ([[91, 60, -91], 0, 300], [0, 1, 2],
243
                               "unrealistic latitude"),
244
                              (None, 4, "unknown method code")])
245
    def test_convert_latlon_arr_failure(self, in_rep, in_irep, msg):
246
        in_args = np.array([self.lat_in, self.lon_in, self.alt_in, self.dtime,
247
                            "G2A"], dtype=object)
248
        in_args[in_irep] = in_rep
249
        with pytest.raises(ValueError, match=msg):
250
            aacgmv2.convert_latlon_arr(*in_args)
251
252
253
class TestGetAACGMCoord:
254
    def setup(self):
255
        """Runs before every method to create a clean testing setup"""
256
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
257
        self.ddate = dt.date(2015, 1, 1)
258
        self.in_args = [60, 0]
259
        self.out = None
260
        self.rtol = 1.0e-4
261
262
    def teardown(self):
263
        """Runs after every method to clean up previous testing"""
264
        del self.out, self.in_args, self.rtol, self.dtime, self.ddate
265
266
    @pytest.mark.parametrize('alt,method_code,ref',
267
                             [(300, 'TRACE', [58.2268, 81.1613, 0.1888]),
268
                              (3000.0, "G2A|BADIDEA", [64.3578, 83.2895,
269
                                                       0.3307]),
270
                              (7000.0, "G2A|TRACE|BADIDEA",
271
                               [69.3187, 85.0845, 0.4503])])
272
    def test_get_aacgm_coord(self, alt, method_code, ref):
273
        """Test single value AACGMV2 calculation, defaults to TRACE"""
274
        self.in_args.extend([alt, self.dtime, method_code])
275
        self.out = aacgmv2.get_aacgm_coord(*self.in_args)
276
        np.testing.assert_allclose(self.out, ref, rtol=self.rtol)
277
278
    def test_get_aacgm_coord_datetime_date(self):
279
        """Test single AACGMV2 calculation with date and datetime input"""
280
        self.in_args.extend([300.0, self.ddate, 'TRACE'])
281
        self.out = aacgmv2.get_aacgm_coord(*self.in_args)
282
        np.testing.assert_allclose(self.out, [58.2268, 81.1613, 0.1888],
283
                                   rtol=self.rtol)
284
285
    @pytest.mark.skipif(version_info.major == 2,
286
                        reason='Not raised in Python 2')
287
    def test_get_aacgm_coord_location_failure(self):
288
        """Test single value AACGMV2 calculation with a bad location"""
289
        self.in_args.extend([0.0, self.dtime, 'TRACE'])
290
        self.in_args[0] = 0.0
291
292
        self.out = aacgmv2.get_aacgm_coord(*self.in_args)
293
        np.all(np.isnan(np.array(self.out)))
294
295
    def test_get_aacgm_coord_maxalt_failure(self):
296
        """test get_aacgm_coord failure for an altitude too high for coeffs"""
297
        self.in_args.extend([2001, self.dtime, ""])
298
        self.out = aacgmv2.get_aacgm_coord(*self.in_args)
299
        assert np.all(np.isnan(np.array(self.out)))
300
301
    @pytest.mark.parametrize('in_index,value',
302
                             [(3, None), (0, 91.0), (0, -91.0)])
303
    def test_get_aacgm_coord_raise_value_error(self, in_index, value):
304
        """Test different ways to raise a ValueError"""
305
        self.in_args.extend([300.0, self.dtime])
306
        self.in_args[in_index] = value
307
        with pytest.raises(ValueError):
308
            self.out = aacgmv2.get_aacgm_coord(*self.in_args)
309
310
311
class TestGetAACGMCoordArr(TestConvertArray):
312 View Code Duplication
    def setup(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
313
        """Runs before every method to create a clean testing setup"""
314
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
315
        self.ddate = dt.date(2015, 1, 1)
316
        self.lat_in = [60.0, 61.0]
317
        self.lon_in = [0.0, 0.0]
318
        self.alt_in = [300.0, 300.0]
319
        self.method = 'TRACE'
320
        self.out = None
321
        self.ref = [[58.22676, 59.31847], [81.16135, 81.60797],
322
                    [0.18880, 0.21857]]
323
        self.rtol = 1.0e-4
324
325
    def teardown(self):
326
        """Runs after every method to clean up previous testing"""
327
        del self.out, self.ref, self.lat_in, self.dtime, self.ddate
328
        del self.lon_in, self.alt_in, self.method, self.rtol
329
330
    def test_get_aacgm_coord_arr_single_val(self):
331
        """Test array AACGMV2 calculation for a single value"""
332
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in[0], self.lon_in[0],
333
                                               self.alt_in[0], self.dtime,
334
                                               self.method)
335
        self.ref = [[rr[0]] for rr in self.ref]
336
        self.evaluate_output()
337
338
    def test_get_aacgm_coord_arr_list_single(self):
339
        """Test array AACGMV2 calculation for list input of single values"""
340
        self.out = aacgmv2.get_aacgm_coord_arr([self.lat_in[0]],
341
                                               [self.lon_in[0]],
342
                                               [self.alt_in[0]], self.dtime,
343
                                               self.method)
344
        self.ref = [[rr[0]] for rr in self.ref]
345
        self.evaluate_output()
346
347
    def test_get_aacgm_coord_arr_arr_single(self):
348
        """Test array AACGMV2 calculation for array with a single value"""
349
        self.out = aacgmv2.get_aacgm_coord_arr(np.array([self.lat_in[0]]),
350
                                               np.array([self.lon_in[0]]),
351
                                               np.array([self.alt_in[0]]),
352
                                               self.dtime, self.method)
353
        self.ref = [[rr[0]] for rr in self.ref]
354
        self.evaluate_output()
355
356
    def test_get_aacgm_coord_arr_list(self):
357
        """Test array AACGMV2 calculation for list input"""
358
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in,
359
                                               self.alt_in, self.dtime,
360
                                               self.method)
361
        self.evaluate_output()
362
363
    def test_get_aacgm_coord_arr_arr(self):
364
        """Test array AACGMV2 calculation for an array"""
365
        self.out = aacgmv2.get_aacgm_coord_arr(np.array(self.lat_in),
366
                                               np.array(self.lon_in),
367
                                               np.array(self.alt_in),
368
                                               self.dtime, self.method)
369
        self.evaluate_output()
370
371
    def test_get_aacgm_coord_arr_list_mix(self):
372
        """Test array AACGMV2 calculation for a list and floats"""
373
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in[0],
374
                                               self.alt_in[0], self.dtime,
375
                                               self.method)
376
        self.evaluate_output()
377
378
    def test_get_aacgm_coord_arr_arr_mix(self):
379
        """Test array AACGMV2 calculation for an array and floats"""
380
        self.out = aacgmv2.get_aacgm_coord_arr(np.array(self.lat_in),
381
                                               self.lon_in[0], self.alt_in[0],
382
                                               self.dtime, self.method)
383
        self.evaluate_output()
384
385
    def test_get_aacgm_coord_arr_badidea(self):
386
        """Test array AACGMV2 calculation for BADIDEA"""
387
        self.method = "|".join([self.method, "BADIDEA"])
388
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in[0], self.lon_in[0],
389
                                               [3000.0], self.dtime,
390
                                               self.method)
391
        self.ref = [[64.3481], [83.2885], [0.3306]]
392
        self.evaluate_output()
393
394
    @pytest.mark.skipif(version_info.major == 2,
395
                        reason='Not raised in Python 2')
396
    def test_get_aacgm_coord_arr_location_failure(self):
397
        """Test array AACGMV2 calculation with a bad location"""
398
        self.out = aacgmv2.get_aacgm_coord_arr([0], [0], [0], self.dtime,
399
                                               self.method)
400
401
        np.testing.assert_equal(len(self.out), len(self.ref))
402
        assert [isinstance(oo, np.ndarray) and len(oo) == 1 for oo in self.out]
403
        assert np.any([np.isnan(oo) for oo in self.out])
404
405
    def test_get_aacgm_coord_arr_mult_failure(self):
406
        """Test aacgm_coord_arr failure with multi-dim array input"""
407
408
        with pytest.raises(ValueError):
409
            (self.mlat_out, self.mlon_out,
410
             self.mlt_out) = aacgmv2.get_aacgm_coord_arr(
411
                 np.array([[60, 61, 62], [63, 64, 65]]), 0, 300, self.dtime)
412
413
    def test_get_aacgm_coord_arr_time_failure(self):
414
        """Test array AACGMV2 calculation with a bad time"""
415
        with pytest.raises(ValueError):
416
            aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in, self.alt_in,
417
                                        None, self.method)
418
419
    def test_get_aacgm_coord_arr_mlat_failure(self):
420
        """Test error return for co-latitudes above 90 for an array"""
421
422
        self.lat_in = [91, 60, -91]
423
        with pytest.raises(ValueError):
424
            self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in[0],
425
                                                   self.alt_in[0], self.dtime,
426
                                                   self.method)
427
428
    def test_get_aacgm_coord_arr_datetime_date(self):
429
        """Test array AACGMV2 calculation with date and datetime input"""
430
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in,
431
                                               self.alt_in, self.ddate,
432
                                               self.method)
433
        self.ref = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in,
434
                                               self.alt_in, self.dtime,
435
                                               self.method)
436
        self.evaluate_output()
437
438
    def test_get_aacgm_coord_arr_maxalt_failure(self):
439
        """test aacgm_coord_arr failure for an altitude too high for coeff"""
440
        self.method = ""
441
        self.alt_in = [2001 for ll in self.lat_in]
442
        self.out = aacgmv2.get_aacgm_coord_arr(self.lat_in, self.lon_in,
443
                                               self.alt_in, self.dtime,
444
                                               self.method)
445
446
        np.testing.assert_equal(len(self.out), len(self.ref))
447
        assert [isinstance(oo, np.ndarray) and len(oo) == len(self.lat_in)
448
                for oo in self.out]
449
        assert np.all(np.isnan(np.array(self.out)))
450
451
452
class TestConvertCode:
453
    def setup(self):
454
        self.c_method_code = None
455
456
    def teardown(self):
457
        del self.c_method_code
458
459
    @pytest.mark.parametrize('method_code',
460
                             [('G2A'), ('A2G'), ('TRACE'), ('ALLOWTRACE'),
461
                              ('BADIDEA'), ('GEOCENTRIC'), ('g2a')])
462
    def test_convert_str_to_bit(self, method_code):
463
        """Test conversion from string code to bit"""
464
        if hasattr(aacgmv2._aacgmv2, method_code.upper()):
465
            self.c_method_code = getattr(aacgmv2._aacgmv2, method_code.upper())
466
        else:
467
            raise ValueError('cannot find method in C code: {:}'.format(
468
                method_code))
469
470
        assert aacgmv2.convert_str_to_bit(method_code) == self.c_method_code
471
472
    def test_convert_str_to_bit_spaces(self):
473
        """Test conversion from string code to bit for a code with spaces"""
474
        if(aacgmv2.convert_str_to_bit("G2A | trace")
475
           != aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE):
476
            raise AssertionError()
477
478
    def test_convert_str_to_bit_invalid(self):
479
        """Test conversion from string code to bit for an invalid code"""
480
        if aacgmv2.convert_str_to_bit("ggoogg|") != aacgmv2._aacgmv2.G2A:
481
            raise AssertionError()
482
483
    @pytest.mark.parametrize('bool_dict,method_code',
484
                             [({}, 'G2A'), ({'a2g': True}, 'A2G'),
485
                              ({'trace': True}, 'TRACE'),
486
                              ({'allowtrace': True}, 'ALLOWTRACE'),
487
                              ({'badidea': True}, 'BADIDEA'),
488
                              ({'geocentric': True}, 'GEOCENTRIC')])
489
    def test_convert_bool_to_bit(self, bool_dict, method_code):
490
        """Test conversion from Boolean code to bit"""
491
        if hasattr(aacgmv2._aacgmv2, method_code.upper()):
492
            self.c_method_code = getattr(aacgmv2._aacgmv2, method_code.upper())
493
        else:
494
            raise ValueError('cannot find method in C code: {:}'.format(
495
                method_code))
496
497
        assert aacgmv2.convert_bool_to_bit(**bool_dict) == self.c_method_code
498
499
500
class TestMLTConvert:
501
    def setup(self):
502
        """Runs before every method to create a clean testing setup"""
503
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
504
        self.dtime2 = dt.datetime(2015, 1, 1, 10, 0, 0)
505
        self.ddate = dt.date(2015, 1, 1)
506
        self.mlon_out = None
507
        self.mlt_out = None
508
        self.mlt_diff = None
509
        self.mlon_list = [270.0, 80.0, -95.0]
510
        self.mlt_list = [12.0, 25.0, -1.0]
511
        self.mlon_comp = [-101.670617955439, 93.329382044561, 63.329382044561]
512
        self.mlt_comp = [12.7780412, 0.11137453, 12.44470786]
513
        self.diff_comp = np.ones(shape=(3,)) * -10.52411552
514
515
    def teardown(self):
516
        """Runs after every method to clean up previous testing"""
517
        del self.mlon_out, self.mlt_out, self.mlt_list, self.mlon_list
518
        del self.mlon_comp, self.mlt_comp, self.mlt_diff, self.diff_comp
519
520
    def test_date_input(self):
521
        """Test to see that the date input works"""
522
        self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.ddate,
523
                                           m2a=False)
524
        np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4)
525
526
    def test_datetime_exception(self):
527
        """Test to see that a value error is raised with bad time input"""
528
        with pytest.raises(ValueError):
529
            self.mlt_out = aacgmv2.wrapper.convert_mlt(self.mlon_list, 1997)
530
531
    def test_inv_convert_mlt_single(self):
532
        """Test MLT inversion for a single value"""
533
        for i, mlt in enumerate(self.mlt_list):
534
            self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True)
535
            np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i],
536
                                           decimal=4)
537
538
    def test_inv_convert_mlt_list(self):
539
        """Test MLT inversion for a list"""
540
        self.mlon_out = aacgmv2.convert_mlt(self.mlt_list, self.dtime, m2a=True)
541
        np.testing.assert_allclose(self.mlon_out, self.mlon_comp, rtol=1.0e-4)
542
543
    def test_inv_convert_mlt_arr(self):
544
        """Test MLT inversion for an array"""
545
        self.mlon_out = aacgmv2.convert_mlt(np.array(self.mlt_list), self.dtime,
546
                                            m2a=True)
547
548
        np.testing.assert_allclose(self.mlon_out, self.mlon_comp, rtol=1.0e-4)
549
550
    def test_inv_convert_mlt_wrapping(self):
551
        """Test MLT wrapping"""
552
        self.mlon_out = aacgmv2.convert_mlt(np.array([1, 25, -1, 23]),
553
                                            self.dtime, m2a=True)
554
555
        np.testing.assert_almost_equal(self.mlon_out[0], self.mlon_out[1],
556
                                       decimal=6)
557
        np.testing.assert_almost_equal(self.mlon_out[2], self.mlon_out[3],
558
                                       decimal=6)
559
560
    def test_mlt_convert_mlon_wrapping(self):
561
        """Test mlon wrapping"""
562
        self.mlt_out = aacgmv2.convert_mlt(np.array([270, -90, 1, 361]),
563
                                           self.dtime, m2a=False)
564
565
        np.testing.assert_almost_equal(self.mlt_out[0], self.mlt_out[1],
566
                                       decimal=6)
567
        np.testing.assert_almost_equal(self.mlt_out[2], self.mlt_out[3],
568
                                       decimal=6)
569
570
    def test_mlt_convert_single(self):
571
        """Test MLT calculation for a single value"""
572
        for i, mlon in enumerate(self.mlon_list):
573
            self.mlt_out = aacgmv2.convert_mlt(mlon, self.dtime, m2a=False)
574
            np.testing.assert_almost_equal(self.mlt_out, self.mlt_comp[i],
575
                                           decimal=4)
576
577
    def test_mlt_convert_list(self):
578
        """Test MLT calculation for a list"""
579
        self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime,
580
                                           m2a=False)
581
        np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4)
582
583
    def test_mlt_convert_arr(self):
584
        """Test MLT calculation for an array"""
585
        self.mlt_out = aacgmv2.convert_mlt(np.array(self.mlon_list),
586
                                           self.dtime, m2a=False)
587
        np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4)
588
589
    def test_mlt_convert_list_w_times(self):
590
        """Test MLT calculation for data and time arrays"""
591
        self.dtime = [self.dtime for dd in self.mlon_list]
592
        self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime,
593
                                           m2a=False)
594
        np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4)
595
596
    def test_mlt_convert_change(self):
597
        """Test that MLT changes with UT"""
598
        self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime)
599
        self.mlt_diff = np.array(self.mlt_out) \
600
            - np.array(aacgmv2.convert_mlt(self.mlon_list, self.dtime2))
601
602
        np.testing.assert_allclose(self.mlt_diff, self.diff_comp, rtol=1.0e-4)
603
604
    def test_mlt_convert_multidim_failure(self):
605
        """Test MLT calculation failure for multi-dimensional arrays"""
606
        self.mlon_list = np.full(shape=(3, 2), fill_value=50.0)
607
        with pytest.raises(ValueError):
608
            aacgmv2.convert_mlt(self.mlon_list, self.dtime, m2a=False)
609
610
    def test_mlt_convert_mismatch_failure(self):
611
        """Test MLT calculation failure for mismatched array input"""
612
        with pytest.raises(ValueError):
613
            aacgmv2.convert_mlt(self.mlon_list, [self.dtime, self.dtime2],
614
                                m2a=False)
615
616
617
class TestCoeffPath:
618
619
    def setup(self):
620
        """Runs before every method to create a clean testing setup"""
621
        os.environ['IGRF_COEFFS'] = "default_igrf"
622
        os.environ['AACGM_v2_DAT_PREFIX'] = "default_coeff"
623
        self.ref = {"igrf_file": os.environ['IGRF_COEFFS'],
624
                    "coeff_prefix": os.environ['AACGM_v2_DAT_PREFIX']}
625
626
    def teardown(self):
627
        """Runs after every method to clean up previous testing"""
628
        del self.ref
629
630
    @pytest.mark.parametrize("in_coeff",
631
                             [({}),
632
                              ({"igrf_file": "hi", "coeff_prefix": "bye"}),
633
                              ({"igrf_file": True, "coeff_prefix": True}),
634
                              ({"coeff_prefix": "hi"}),
635
                              ({"igrf_file": "hi"}),
636
                              ({"igrf_file": None, "coeff_prefix": None})])
637
    def test_set_coeff_path(self, in_coeff):
638
        """Test the coefficient path setting using default values"""
639
        # Update the reference key, if needed
640
        for ref_key in in_coeff.keys():
641
            if in_coeff[ref_key] is True or in_coeff[ref_key] is None:
642
                if ref_key == "igrf_file":
643
                    self.ref[ref_key] = aacgmv2.IGRF_COEFFS
644
                elif ref_key == "coeff_prefix":
645
                    self.ref[ref_key] = aacgmv2.AACGM_v2_DAT_PREFIX
646
            else:
647
                self.ref[ref_key] = in_coeff[ref_key]
648
649
        # Run the routine
650
        aacgmv2.wrapper.set_coeff_path(**in_coeff)
651
652
        # Ensure the environment variables were set correctly
653
        if os.environ['IGRF_COEFFS'] != self.ref['igrf_file']:
654
            raise AssertionError("{:} != {:}".format(os.environ['IGRF_COEFFS'],
655
                                                     self.ref['igrf_file']))
656
        if os.environ['AACGM_v2_DAT_PREFIX'] != self.ref['coeff_prefix']:
657
            raise AssertionError("{:} != {:}".format(
658
                os.environ['AACGM_v2_DAT_PREFIX'], self.ref['coeff_prefix']))
659
660
661
class TestHeightReturns:
662
    def setup(self):
663
        """Runs before every method to create a clean testing setup"""
664
        self.code = aacgmv2._aacgmv2.A2G
665
        self.bad_code = aacgmv2._aacgmv2.BADIDEA
666
        self.trace_code = aacgmv2._aacgmv2.TRACE
667
668
    def teardown(self):
669
        """Runs after every method to clean up previous testing"""
670
        del self.code, self.bad_code
671
672
    def test_low_height_good(self):
673
        """ Test to see that a very low height is still accepted"""
674
675
        assert aacgmv2.wrapper.test_height(-1, self.code)
676
677
    def test_high_coeff_bad(self):
678
        """ Test to see that a high altitude for coefficent use fails"""
679
680
        assert not aacgmv2.wrapper.test_height(aacgmv2.high_alt_coeff + 10.0,
681
                                               self.code)
682
683
    def test_high_coeff_good(self):
684
        """ Test a high altitude for coefficent use with badidea """
685
686
        assert aacgmv2.wrapper.test_height(aacgmv2.high_alt_coeff + 10.0,
687
                                           self.bad_code)
688
689
    def test_low_coeff_good(self):
690
        """ Test that a normal height succeeds"""
691
        assert aacgmv2.wrapper.test_height(aacgmv2.high_alt_coeff * 0.5,
692
                                           self.code)
693
694
    def test_high_trace_bad(self):
695
        """ Test that a high trace height fails"""
696
        assert not aacgmv2.wrapper.test_height(aacgmv2.high_alt_trace + 10.0,
697
                                               self.code)
698
699
    def test_low_trace_good(self):
700
        """ Test that a high coefficient height succeeds with trace"""
701
        assert aacgmv2.wrapper.test_height(aacgmv2.high_alt_coeff + 10.0,
702
                                           self.trace_code)
703
704
    def test_high_trace_good(self):
705
        """ Test that a high trace height succeeds with badidea"""
706
        assert aacgmv2.wrapper.test_height(aacgmv2.high_alt_trace + 10.0,
707
                                           self.bad_code)
708
709
710
class TestPyLogging:
711
    def setup(self):
712
        """Runs before every method to create a clean testing setup"""
713
714
        self.lwarn = u""
715
        self.lout = u""
716
        self.log_capture = StringIO()
717
        aacgmv2.logger.addHandler(logging.StreamHandler(self.log_capture))
718
        aacgmv2.logger.setLevel(logging.INFO)
719
720
    def teardown(self):
721
        """Runs after every method to clean up previous testing"""
722
        self.log_capture.close()
723
        del self.lwarn, self.lout, self.log_capture
724
725
    def test_warning_below_ground(self):
726
        """ Test that a warning is issued if height < 0 for height test """
727
        self.lwarn = u"conversion not intended for altitudes < 0 km"
728
729
        aacgmv2.wrapper.test_height(-1, 0)
730
        self.lout = self.log_capture.getvalue()
731
        if self.lout.find(self.lwarn) < 0:
732
            raise AssertionError()
733
734
    def test_warning_magnetosphere(self):
735
        """ Test that a warning is issued if altitude is very high"""
736
        self.lwarn = u"coordinates are not intended for the magnetosphere"
737
738
        aacgmv2.wrapper.test_height(70000, aacgmv2._aacgmv2.TRACE)
739
        self.lout = self.log_capture.getvalue()
740
        if self.lout.find(self.lwarn) < 0:
741
            raise AssertionError()
742
743
    def test_warning_high_coeff(self):
744
        """ Test that a warning is issued if altitude is very high"""
745
        self.lwarn = u"must either use field-line tracing (trace=True"
746
747
        aacgmv2.wrapper.test_height(3000, 0)
748
        self.lout = self.log_capture.getvalue()
749
        if self.lout.find(self.lwarn) < 0:
750
            raise AssertionError()
751
752
    def test_warning_single_loc_in_arr(self):
753
        """ Test that user is warned they should be using simpler routine"""
754
        self.lwarn = u"for a single location, consider using"
755
756
        aacgmv2.convert_latlon_arr(60, 0, 300, dt.datetime(2015, 1, 1, 0, 0, 0))
757
        self.lout = self.log_capture.getvalue()
758
        if self.lout.find(self.lwarn) < 0:
759
            raise AssertionError()
760
761
762
class TestTimeReturns:
763
    def setup(self):
764
        """Runs before every method to create a clean testing setup"""
765
        self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0)
766
        self.dtime2 = dt.datetime(2015, 1, 1, 10, 10, 10)
767
        self.ddate = dt.date(2015, 1, 1)
768
769
    def teardown(self):
770
        """Runs after every method to clean up previous testing"""
771
        del self.dtime, self.ddate, self.dtime2
772
773
    def test_good_time(self):
774
        """ Test to see that a good datetime is accepted"""
775
776
        assert self.dtime == aacgmv2.wrapper.test_time(self.dtime)
777
778
    def test_good_time_with_nonzero_time(self):
779
        """ Test to see that a good datetime with h/m/s is accepted"""
780
781
        assert self.dtime2 == aacgmv2.wrapper.test_time(self.dtime2)
782
783
    def test_good_date(self):
784
        """ Test to see that a good date has a good datetime output"""
785
786
        assert self.dtime == aacgmv2.wrapper.test_time(self.dtime)
787
788
    def test_bad_time(self):
789
        """ Test to see that a warning is raised with a bad time input"""
790
        with pytest.raises(ValueError):
791
            aacgmv2.wrapper.test_time(2015)
792