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

4Unit tests for ``llama.listen.gcn``. 

5""" 

6 

7import os 

8from abc import abstractproperty 

9import tempfile 

10import logging 

11from copy import copy 

12from warnings import warn 

13from datetime import datetime 

14from subprocess import Popen, PIPE 

15from time import sleep, time 

16from llama.run import Run 

17from llama.pipeline import Pipeline 

18from llama.listen.gcn import LlamaHandlers, run_on_voevent_file, get_subdir 

19from llama.filehandler import FileGraph 

20from llama.test.classes import ( 

21 AbstractFileGenerationComparator, 

22 MS181101abMixin, 

23 S190425zMixin, 

24 S190521gMixin, 

25) 

26from llama.files.skymap_info import SkymapInfo 

27from llama.files.lvc_gcn_xml import LvcGcnXml 

28from llama.files.advok import Advok 

29 

30LOGGER = logging.getLogger(__name__) 

31 

32 

33# pylint: disable=abstract-method 

34class AbstractTestLlamaHandlers(AbstractFileGenerationComparator): 

35 """ 

36 Test LlamaHandlers with example GCN Notices to make sure that they are 

37 parsed correctly. Implementations of this class must provide an ``EVENTID`` 

38 class variable specifying the ``eventid`` to use (the ``rundir`` will be a 

39 temporary directory and the data source will be in the test data 

40 directory). 

41 """ 

42 

43 STARTING_MANIFEST = () 

44 

45 @property 

46 def pipeline(self): 

47 return Pipeline( 

48 SkymapInfo=SkymapInfo, 

49 LvcGcnXml=LvcGcnXml, 

50 ) 

51 

52 @property 

53 def gcn_archive(self): 

54 """A temporary directory for storing GCN archived files.""" 

55 return getattr(self, '_gcn_archive', None) 

56 

57 @gcn_archive.setter 

58 def gcn_archive(self, gcn_archive): 

59 """Set the gcn_archive value.""" 

60 setattr(self, '_gcn_archive', gcn_archive) 

61 

62 @property 

63 def handler(self): 

64 """Get a ``LlamaHandlers`` instance for this test class.""" 

65 return LlamaHandlers(rundir=self.run.rundir, 

66 gcn_archive=self.gcn_archive) 

67 

68 def execute(self): 

69 """Test whether the LLAMA handler properly creates the expected event 

70 directory and output files by comparing what is created to the expected 

71 output.""" 

72 filehandler = LvcGcnXml(self.inputevent) 

73 run_on_voevent_file(files=filehandler.fullpath, handler=self.handler) 

74 

75 def test(self): 

76 with tempfile.TemporaryDirectory() as tmpdir: 

77 self.gcn_archive = tmpdir 

78 # if you run the test at midnight, the files might be different 

79 # based on the date switch, so look for files in both places in 

80 # case the date switches in the middle of the test 

81 start = datetime.utcnow() 

82 super().test() 

83 end = datetime.utcnow() 

84 # might get archived twice if the test received VOEvents before and 

85 # after the date change (midnight UTC); check both in that case. 

86 for outd in {get_subdir(tmpdir, n) for n in (start, end)}: 

87 outfiles = [os.path.join(outd, f) for f in os.listdir(outd)] 

88 # test whether files were archived properly 

89 assert len(outfiles) == 1, "Expect exactly 1 archived event." 

90 archived = outfiles.pop() 

91 voe = LvcGcnXml(self.inputevent) 

92 if not voe.compare_contents(archived): 

93 warn(("{} and {} differ in GCN archive test. Diff:\n" 

94 "{}").format(voe.fullpath, archived, 

95 voe.diff_contents(archived))) 

96 raise AssertionError(("{} and {} differ." 

97 "").format(voe.fullpath, archived)) 

98 

99 

100class GcnInitialMixin(AbstractTestLlamaHandlers): 

101 """Specifies that this tests for LVC *Initial* GCN Notices; the proper 

102 ``LvcGcnXml``, ``SkymapInfo``, and ``Advok`` files should be generated.""" 

103 

104 @property 

105 def pipeline(self): 

106 return super().pipeline + Pipeline(Advok=Advok) 

107 

108 

109class AbstractGcndTester(AbstractTestLlamaHandlers): 

110 """ 

111 Test ``gcnd``'s ability to listen for an parse events. ``gcnd`` has to be 

112 in your "$PATH" for this to work along with ``pygcn-serve``. Tests the same 

113 thing as ``AbstractFileGenerationComparator``, but does so in a more robust 

114 way by using the command line interface to test the script's functionality. 

115 """ 

116 

117 SLEEP_INTERVAL = 0.25 

118 TIMEOUT = 6 

119 

120 def execute(self): 

121 """ 

122 Launch ``gcnd`` and make it listen to a test server set up with 

123 ``pygcn-serve``. Let run for 5 seconds before timing out and killing. 

124 """ 

125 env = copy(os.environ) 

126 env['PYTEST'] = 'true' 

127 server = Popen(['pygcn-serve', LvcGcnXml(self.inputevent).fullpath], 

128 stdout=PIPE, stderr=PIPE) 

129 listener = Popen(['llama', 'listen', 'gcn', '-r', self.run.rundir, 

130 '-g', self.gcn_archive, '-i', '127.0.0.1'], 

131 stdout=PIPE, stderr=PIPE, env=env) 

132 start = time() 

133 # only keep it running till the LLAMA handler and the archive handler 

134 # have saved their expected outputs 

135 outfiles = self.pipeline.file_handler_instances(self.event) 

136 def done(): 

137 return (all(f.exists() for f in outfiles.values()) and 

138 os.listdir(get_subdir(self.gcn_archive, 

139 datetime.utcnow()))) 

140 while time() - start < self.TIMEOUT and not done(): 

141 sleep(self.SLEEP_INTERVAL) 

142 listener.kill() 

143 server.kill() 

144 print("Server STDOUT:\n%s\nSTDERR:\n%s", *(r.decode() for r in 

145 server.communicate())) 

146 print("Listener STDOUT:\n%s\nSTDERR:\n%s", *(r.decode() for r in 

147 listener.communicate())) 

148 # seems there are some race conditions associated with git still 

149 # operating on those directories. see if this helps. 

150 sleep(0.3) 

151 

152 

153class TestLlamaHandlerMS181101ab(MS181101abMixin, GcnInitialMixin): 

154 """Test ``LlamaHandlers`` on a sample GCN Initial notice for ER14.""" 

155 

156 

157class TestLlamaHandlerS190425z(S190425zMixin, GcnInitialMixin): 

158 """ 

159 Test ``LlamaHandlers`` on the GCN Initial notice that came out with 

160 the first BNS event of O3 on April 25, 2019 

161 """ 

162 

163 

164class TestLlamaHandlerS190521g(S190521gMixin, AbstractTestLlamaHandlers): 

165 """ 

166 Test ``LlamaHandlers`` parser on the GCN Preliminary notice that came 

167 out with an early-O3 BBH event from May 21, 2019. 

168 """ 

169 

170 

171class TestGcndS190425z(S190425zMixin, GcnInitialMixin): 

172 """ 

173 Test the daemon on the GCN Initial notice that came out with the first 

174 BNS event of O3 on April 25, 2019. 

175 """ 

176 

177 

178class TestGcndS190521g(S190521gMixin, AbstractGcndTester): 

179 """ 

180 Test the daemon on the GCN Preliminary notice that came out an early-O3 

181 BBH event from May 21, 2019. 

182 """