Hide keyboard shortcuts

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 

2 

3""" 

4Test various LLAMA command line executables/scripts. 

5""" 

6 

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 

25 

26LOGGER = logging.getLogger(__name__) 

27 

28 

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}" 

55 

56 

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'] 

77 

78 

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 """ 

87 

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 

95 

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 

116 

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") 

130 

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") 

142 

143 @property 

144 def pipeline(self): 

145 return Pipeline(**{ 

146 "SkymapInfo": SkymapInfo, 

147 }) 

148 

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 

170 

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) 

176 

177 

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) 

239 

240 

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 """ 

248 

249 SKYMAP_FILENAME = "bayestar.fits,0" 

250 

251 

252class TestS190412mSkymapInfoCliTest(AbstractTestS190412mSkymapInfoCli): 

253 """ 

254 ``AbstractTestS190412mSkymapInfoCli`` with ``TRIGGERED_PUBLIC`` flag 

255 preset. 

256 """ 

257 

258 FLAGS_PRESET = 'TRIGGERED_TEST' 

259 

260 

261class TestS190412mSkymapInfoCliPublic(AbstractTestS190412mSkymapInfoCli): 

262 """ 

263 ``AbstractTestS190412mSkymapInfoCli`` with ``TRIGGERED_PUBLIC`` flag 

264 preset. 

265 """ 

266 

267 FLAGS_PRESET = 'TRIGGERED_PUBLIC'