Passed
Pull Request — develop (#112)
by Angeline
01:37
created

test_cmd.TestCommandLine.execute_command_line()   B

Complexity

Conditions 6

Size

Total Lines 40
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
nop 4
dl 0
loc 40
rs 8.6666
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
"""Unit tests for command line execution."""
3
4
import numpy as np
5
import os
6
import pytest
7
import subprocess
8
9
10
class TestCommandLine(object):
11
    """Test class for the command-line apexpy interface."""
12
13
    def setup_method(self):
14
        """Runs before every test method to create a clean environment."""
15
        # Define the desired working paths
16
        self.startdir = os.path.abspath(os.path.curdir)
17
        split_dirs = os.path.split(os.path.dirname(os.path.abspath(__file__)))
18
        self.workdir = split_dirs[0]
19
20
        # Change directory, if needed
21
        if self.startdir != self.workdir:
22
            os.chdir(self.workdir)
23
24
        # Define the test filenames
25
        self.outfile = os.path.join(split_dirs[1], 'output.txt')
26
        self.infile = os.path.join(split_dirs[1], 'test_convert.txt')
27
        self.singlefile = os.path.join(split_dirs[1],
28
                                       'test_convert_single_line.txt')
29
30
    def teardown_method(self):
31
        """Runs after every method to clean up previous testing."""
32
        # Remove testing output
33
        if os.path.isfile(self.outfile):
34
            os.remove(self.outfile)
35
36
        # Return to starting directory
37
        if self.startdir != os.path.abspath(os.path.curdir):
38
            os.chdir(self.startdir)
39
40
        del self.outfile, self.infile, self.singlefile
41
42
    def execute_command_line(self, command, command_kwargs=None,
43
                             pipe_out=False):
44
        """Execute the command and load data from self.outfile
45
46
        Parameters
47
        ----------
48
        command : list or str
49
            List or string containing command to execute using subprocess
50
        command_kwargs : dict or NoneType
51
            Dict containing optional kwargs for subprocess.Popen command or
52
            None if using defaults. (default=None)
53
        pipe_out : bool
54
            Return pipe output instead of output from a data file if True
55
            (default=False)
56
57
        Returns
58
        -------
59
        data : np.array, NoneType, or subprocess.Popen attribute
60
            Numpy array of data from output file, None if no file was created,
61
            or the requested output from the pipe command.
62
63
        """
64
        if command_kwargs is None:
65
            command_kwargs = {}
66
67
        pipe = subprocess.Popen(command, **command_kwargs)
68
        out = pipe.communicate()
69
        pipe.wait()
70
71
        data = None
72
        if pipe_out:
73
            data = out
74
        elif os.path.isfile(self.outfile):
75
            data = np.loadtxt(self.outfile)
76
77
            if data is not None and len(data) == 0:
78
                raise RuntimeError(data,
79
                                   os.system("more {:s}".format(self.outfile)))
80
81
        return data
82
83
    @pytest.mark.parametrize("date_str", [("2015"), ("201501"), ('20150101'),
84
                                          ('20150101000000')])
85
    def test_convert_w_datetime(self, date_str):
86
        """Test command line with different date and time specification.
87
88
        Parameters
89
        ----------
90
        date_str : str
91
           Input date string
92
93
        """
94
        # Build and execute the apexpy command line call
95
        cmd = ['python', '-m', 'apexpy', 'geo', 'apex', date_str, '--height',
96
               '300', '-i', self.infile, '-o', self.outfile]
97
        data = self.execute_command_line(cmd)
98
99
        # Test the outfile existance and values
100
        assert data is not None, 'error executing: {:s}'.format(' '.join(cmd))
101
        np.testing.assert_allclose(data, [[57.47145462, 93.62657928],
102
                                          [58.52458191, 94.03150177],
103
                                          [59.57331467, 94.46398163]],
104
                                   rtol=1e-4)
105
        return
106
107
    def test_convert_single_line(self):
108
        """Test command line with a single line of output."""
109
        # Build and execute the apexpy command line call
110
        cmd = ['python', '-m', 'apexpy', 'geo', 'apex', '20150101000000',
111
               '--height', '300', '-i', self.singlefile, '-o', self.outfile]
112
        data = self.execute_command_line(cmd)
113
114
        # Test the outfile existance and values
115
        assert data is not None, 'error executing: {:s}'.format(' '.join(cmd))
116
        np.testing.assert_allclose(data, [57.47145462, 93.62657928], rtol=1e-4)
117
        return
118
119
    @pytest.mark.parametrize("height, out_list",
120
                             [("300", [57.47145462, 93.62657928]),
121
                              ("100 --refh=300", [56.01779556, 93.35305023])])
122
    def test_convert_stdin_stdout_w_height_flags(self, height, out_list):
123
        """Test use of pipe input to command-line call with height flags.
124
125
        Parameters
126
        ----------
127
        height : str
128
            String specifying height with command line options
129
        out_list : list
130
            List of expected output values
131
132
        """
133
        # Build and execute the apexpy command line call
134
        cmd = ''.join(['echo 60 15 | python -m apexpy geo apex 2015 --height ',
135
                       '{:s}'.format(height)])
136
        cmd_kwargs = {'shell': True, 'stdout': subprocess.PIPE}
137
        stdout, _ = self.execute_command_line(cmd, cmd_kwargs, True)
138
139
        assert stdout is not None, 'error executing: {:s}'.format(' '.join(cmd))
140
        np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float),
141
                                   out_list, rtol=1e-4)
142
        return
143
144
    def test_convert_mlt(self):
145
        """Test magnetic local time conversion."""
146
        # Build and execute the apexpy command line call
147
        cmd = ['python', '-m', 'apexpy', 'geo', 'mlt', '20150101000000',
148
               '--height', '300', '-i', self.singlefile, '-o', self.outfile]
149
        data = self.execute_command_line(cmd)
150
151
        # Test the outfile existance and values
152
        assert data is not None, 'error executing: {:s}'.format(' '.join(cmd))
153
        np.testing.assert_allclose(data, [57.469547, 1.06324], rtol=1e-4)
154
        return
155
156
    @pytest.mark.parametrize("date_str", [("201501010"), ("2015010100000")])
157
    def test_invalid_date(self, date_str):
158
        """Test raises ValueError with an invalid input date.
159
160
        Parameters
161
        ----------
162
        date_str : str
163
           Input date string
164
165
        """
166
        # Build and execute the command
167
        cmd = 'echo 60 15 | python -m apexpy geo apex {:s}'.format(date_str)
168
        cmd_kwargs = {'shell': True, 'stderr': subprocess.PIPE}
169
        _, stderr = self.execute_command_line(cmd, cmd_kwargs, True)
170
171
        # Evaluate the error output
172
        assert stderr is not None, 'error executing: {:s}'.format(' '.join(cmd))
173
        assert b'ValueError' in stderr, 'invalid date error not raised'
174
        return
175
176
    def test_mlt_nodatetime(self):
177
        """Test raises ValueError when time not provided for MLT calc."""
178
        # Build and execute the command
179
        cmd = 'echo 60 15 | python -m apexpy geo mlt 20150101'
180
        cmd_kwargs = {'shell': True, 'stderr': subprocess.PIPE}
181
        _, stderr = self.execute_command_line(cmd, cmd_kwargs, True)
182
183
        # Evaluate the error output
184
        assert stderr is not None, 'error executing: {:s}'.format(' '.join(cmd))
185
        assert b'ValueError' in stderr, 'invalid time error not raised'
186
        return
187
188
    @pytest.mark.parametrize("coords", [("foobar apex"), ("geo foobar")])
189
    def test_invalid_coord(self, coords):
190
        """Test raises error when bad coordinate input provided.
191
192
        Parameters
193
        ----------
194
        coords : str
195
           Input/output coordinate pairs
196
197
        """
198
        # Build and execute the command
199
        cmd = 'echo 60 15 | python -m apexpy {:s} 2015'.format(coords)
200
        cmd_kwargs = {'shell': True, 'stderr': subprocess.PIPE}
201
        _, stderr = self.execute_command_line(cmd, cmd_kwargs, True)
202
203
        # Evaluate the error output
204
        assert stderr is not None, 'error executing: {:s}'.format(' '.join(cmd))
205
        assert b'invalid choice' in stderr, 'invalid coord error not raised'
206
        return
207