Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# (c) Stefan Countryman, 2019
3"""
4Test various LLAMA command line executables/scripts.
5"""
7import os
8import logging
9import json
10from llama.utils import DEFAULT_RUN_DIR
11from llama.files.skymap_info import SkymapInfo
12from llama.files.i3.json import IceCubeNeutrinoList
13from llama.files.i3.txt import IceCubeNeutrinoListCoincTxt
14from llama.files.skymap_info.__main__ import run_on_args
15from llama.pipeline import Pipeline, DEFAULT_PIPELINE, Parsers as PipeParsers
16from llama.flags import FLAG_PRESETS
17from llama.test import floateql
18from llama.test.classes import (
19 AbstractFileGenerationComparator,
20 S190412mMixin,
21)
22from llama.files.i3.json import DELTAT_MINUS, DELTAT_PLUS
23from llama.files.i3.__main__ import mjd_interval
24from llama.run import DEFAULT_EVENT_GLOB, Parsers
26LOGGER = logging.getLogger(__name__)
29def test_llama_pipeline_parser():
30 """Test the ``llama.pipeline.Parsers.pipeline`` parsers with various
31 command line inputs. Make sure they parse the expected ``Pipeline``
32 instances."""
33 # map command line arguments to the expected pipeline value.
34 expected = {
35 ('-p', 'DEFAULT_PIPELINE'): DEFAULT_PIPELINE,
36 ('-p', 'DEFAULT_PIPELINE', '-f', 'SkymapInfo'): DEFAULT_PIPELINE,
37 ('-p', 'DEFAULT_PIPELINE', '+f',
38 'IceCubeNeutrinoList'): DEFAULT_PIPELINE,
39 ('+f', 'IceCubeNeutrinoList'): Pipeline(SkymapInfo,
40 IceCubeNeutrinoList),
41 ('-f', 'IceCubeNeutrinoList'): Pipeline(IceCubeNeutrinoList),
42 ('-f', 'IceCubeNeutrinoList', 'SkymapInfo'): Pipeline(
43 SkymapInfo, IceCubeNeutrinoList),
44 ('-f',): Pipeline(),
45 ('-f', 'IceCubeNeutrinoListCoincTxt',
46 '+f', 'IceCubeNeutrinoList'): Pipeline(SkymapInfo,
47 IceCubeNeutrinoList,
48 IceCubeNeutrinoListCoincTxt),
49 tuple(): DEFAULT_PIPELINE,
50 }
51 for args, parsed in expected.items():
52 namespace = PipeParsers.pipeline.parse_args(list(args))
53 assert isinstance(namespace.pipeline, Pipeline), f"FAILING ARGS {args}"
54 assert namespace.pipeline == parsed, f"FAILING ARGS {args}"
57def test_llama_run_parser():
58 """Test the ``llama.run.Parsers.eventfiltering`` CLI parser, which
59 interprets which directories a run should operate on, by making sure
60 it parses inputs as expected."""
61 # map command line arguments to a list of tuples of expected rundir and
62 # downselection eventid_filter for the runs output.
63 pwd = os.path.realpath(os.getcwd())
64 default_rundir = os.path.realpath(DEFAULT_RUN_DIR)
65 expected = {
66 tuple(): ((default_rundir, DEFAULT_EVENT_GLOB),),
67 ('/.',): ((default_rundir, '.'),),
68 ('./',): ((os.path.realpath(pwd), DEFAULT_EVENT_GLOB),),
69 ('.',): ((os.path.dirname(pwd), os.path.basename(pwd)),),
70 }
71 for args, parsed in expected.items():
72 namespace = Parsers().eventfiltering.parse_args(list(args))
73 assert len(parsed) == len(namespace.run)
74 for i, run in enumerate(namespace.run):
75 assert parsed[i][0] == run.rundir
76 assert parsed[i][1] == run.downselection[0]['eventid_filter']
79# pylint: disable=abstract-method
80class AbstractTestSkymapInfoCli(AbstractFileGenerationComparator):
81 """
82 Test the ability of the ``skymap_info`` command line interface to
83 correctly register and save a new event. Specify different cli_args, FLAGS,
84 and FLAGS_PRESET to test different cases (though note that only one of
85 FLAGS or FLAGS_PRESET should be overridden with non-None values).
86 """
88 STARTING_MANIFEST = tuple()
89 NON_DETERMINISTIC_JSON_FIELDS = [
90 "notice_time_iso",
91 ]
92 FLAGS = None
93 FLAGS_PRESET = None
94 SKYMAP_FILENAME = None
96 @property
97 def cli_args(self):
98 """
99 Specify the command-line arguments (besides ``--flags-preset``,
100 which is handled by ``FLAGS_PRESET``, and ``--flags``, which is handled
101 by ``FLAGS``; both of these will be prepended to ``cli_args``) to pass
102 to ``run_on_args`` when testing the ``skymap_info`` CLI. By default,
103 the output directory, skymap_filename (if it is specified as something
104 other than ``None`` in ``SKYMAP_FILENAME``), and graceid are the only
105 things specified.
106 """
107 args = [
108 '--outdir',
109 self.event.eventdir,
110 '--graceid',
111 self.EVENTID
112 ]
113 if self.SKYMAP_FILENAME is not None:
114 args += ['--skymap', self.SKYMAP_FILENAME]
115 return args
117 @property
118 def cli_flags(self):
119 """Get the flag-specification command-line arguments for this test by
120 constructing them from either ``self.FLAGS`` or ``self.FLAGS_PRESET``.
121 Exactly one of these must be specified or else an assertion will
122 fail."""
123 assert (self.FLAGS is None) or (self.FLAGS_PRESET is None)
124 flags = ['--flags']
125 if self.FLAGS is not None:
126 return flags + [k+'='+v for k, v in self.FLAGS.items()]
127 if self.FLAGS_PRESET is not None:
128 return flags + [self.FLAGS_PRESET]
129 raise AssertionError("Must specify either FLAGS or FLAGS_PRESET")
131 @property
132 def spec_flags(self):
133 """Get a dictionary of the flags specified by either FLAGS or
134 FLAGS_PRESET with which to check the output flag values. Exactly one of
135 these must be specified or else an assertion will fail."""
136 assert (self.FLAGS is None) or (self.FLAGS_PRESET is None)
137 if self.FLAGS is not None:
138 return self.FLAGS
139 if self.FLAGS_PRESET is not None:
140 return dict(getattr(FLAG_PRESETS, self.FLAGS_PRESET))
141 raise AssertionError("Must specify either FLAGS or FLAGS_PRESET")
143 @property
144 def pipeline(self):
145 return Pipeline(**{
146 "SkymapInfo": SkymapInfo,
147 })
149 def compare(self):
150 """We are comparing two JSON files, but some of the contents are
151 non-deterministic; ignore those fields and compare the outputs as JSON
152 dictionaries. Also checks that the saved flag values are as
153 expected."""
154 with self.event.files.SkymapInfo.open() as outfile:
155 outdict = json.load(outfile)
156 with self.inputevent.files.SkymapInfo.open() as infile:
157 indict = json.load(infile)
158 assert set(outdict) == set(indict)
159 for key in outdict:
160 if key not in self.NON_DETERMINISTIC_JSON_FIELDS:
161 if not outdict[key] == indict[key]:
162 fmt = ("Expected equal JSON values for key={key}; instead,"
163 " got indict[{key}]={inval}, "
164 "outdict[{key}]={outval}.")
165 raise AssertionError(fmt.format(key=key, inval=indict[key],
166 outval=outdict[key]))
167 for flag, value in self.spec_flags.items():
168 assert self.event.flags[flag] == value
169 return True
171 def execute(self):
172 """Run the ``skymap_info`` CLI to generate the expected output file."""
173 cmd = self.cli_flags + self.cli_args
174 LOGGER.info("Testing ``skymap_info`` CLI with args: %s", cmd)
175 run_on_args(cliargs=cmd)
178def test_mjd_interval():
179 """Make sure ``llama.files.i3.__main__.mjd_interval`` correctly parses
180 input times making sure that a few equivalent input argument combinations
181 produce the same output."""
182 utc = "2019-04-01T00:00:00"
183 utc_window = ("2019-03-31T23:51:40", "2019-04-01T00:08:20")
184 mjd = 58574.0
185 gps = 1238112018.0
186 days_per_sec = 1/ 86400
187 default_window = (mjd-500*days_per_sec, mjd+500*days_per_sec)
188 # (time, interval, mjd), (mjd_start, mjd_end)
189 in_out = (
190 (
191 (mjd, None, True),
192 default_window
193 ), (
194 (gps, None, False),
195 default_window
196 ), (
197 (utc, None, False),
198 default_window
199 ), (
200 (
201 mjd,
202 (-DELTAT_MINUS*days_per_sec, +DELTAT_PLUS*days_per_sec),
203 True
204 ),
205 default_window
206 ), (
207 (gps, (-DELTAT_MINUS, +DELTAT_PLUS), False),
208 default_window
209 ), (
210 (
211 utc,
212 (-DELTAT_MINUS*days_per_sec, +DELTAT_PLUS*days_per_sec),
213 True
214 ),
215 default_window
216 ), (
217 (utc, (-DELTAT_MINUS, +DELTAT_PLUS), False),
218 default_window
219 ), (
220 (
221 None,
222 (mjd-DELTAT_MINUS*days_per_sec, mjd+DELTAT_PLUS*days_per_sec),
223 True
224 ),
225 default_window
226 ), (
227 (None, (gps-DELTAT_MINUS, gps+DELTAT_PLUS), False),
228 default_window
229 ), (
230 (None, utc_window, True),
231 default_window
232 ), (
233 (None, utc_window, False),
234 default_window
235 ),
236 )
237 for args, out in in_out:
238 assert floateql(mjd_interval(*args), out, prec=1e-10)
241class AbstractTestS190412mSkymapInfoCli(S190412mMixin,
242 AbstractTestSkymapInfoCli):
243 """
244 Test our ability to generate the correct ``skymap_info.json`` file through
245 the ``skymap_info`` CLI for ``S190412m``. Subclass this with specific flag
246 combinations to test multiple cases.
247 """
249 SKYMAP_FILENAME = "bayestar.fits,0"
252class TestS190412mSkymapInfoCliTest(AbstractTestS190412mSkymapInfoCli):
253 """
254 ``AbstractTestS190412mSkymapInfoCli`` with ``TRIGGERED_PUBLIC`` flag
255 preset.
256 """
258 FLAGS_PRESET = 'TRIGGERED_TEST'
261class TestS190412mSkymapInfoCliPublic(AbstractTestS190412mSkymapInfoCli):
262 """
263 ``AbstractTestS190412mSkymapInfoCli`` with ``TRIGGERED_PUBLIC`` flag
264 preset.
265 """
267 FLAGS_PRESET = 'TRIGGERED_PUBLIC'