~mdrews/maus/my-branch

663.33.2 by Chris Rogers
Maybe passes tests
1
#  This file is part of MAUS: http://micewww.pp.rl.ac.uk/projects/maus
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
2
#
3
#  MAUS is free software: you can redistribute it and/or modify
4
#  it under the terms of the GNU General Public License as published by
5
#  the Free Software Foundation, either version 3 of the License, or
6
#  (at your option) any later version.
7
#
8
#  MAUS is distributed in the hope that it will be useful,
9
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
#  GNU General Public License for more details.
12
#
13
#  You should have received a copy of the GNU General Public License
14
#  along with MAUS.  If not, see <http://www.gnu.org/licenses/>.
15
660.1.41 by Chris Rogers
test_root_io - throwing horrid segmentation faults etc and doesnt seem to be doing things right for root_to_json
16
# pylint: disable=C0103
17
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
18
"""
19
Test for OutputCppRoot
20
"""
21
660.1.40 by Chris Rogers
Inputter and Outputter okay
22
import os
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
23
import unittest
24
import json
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
25
26
import ROOT
27
28
import ErrorHandler
29
import Configuration
660.1.7 by Chris Rogers
Builds and imports OutputCppRoot into python okay
30
import OutputCppRoot
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
31
import maus_cpp.globals 
660.1.7 by Chris Rogers
Builds and imports OutputCppRoot into python okay
32
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
33
class TestOutputCppRoot(unittest.TestCase): # pylint: disable=R0904, R0902
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
34
    """
35
    Test we can write out ROOT tree.
36
37
    Check we can birth and death correctly; check we can write simple ROOT trees
38
    especially in context of - how are errors handled - 
39
    """
40
41
    def setUp(self): # pylint: disable=C0103, C0202
42
        """
43
        Define cards and initialise Output
44
        """
45
        self.output = OutputCppRoot.OutputCppRoot()
663.33.2 by Chris Rogers
Maybe passes tests
46
        self.outdir = os.environ["MAUS_ROOT_DIR"]+"/tmp/test_output_cpp_root/"
47
        self.outfile = self.outdir+"test_outputCppRoot.root"
48
        try:
49
            os.mkdir(self.outdir)
50
        except OSError:
51
            pass
52
        try:
53
            os.mkdir(self.outdir+"/end_of_run/")
54
        except OSError:
55
            pass
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
56
        self.on_error_standard = ErrorHandler.DefaultHandler().on_error
663.9.168 by Chris Rogers
Fix style violations
57
        #ref = {"$ref":"#test_branch/double_by_value"}
660.1.40 by Chris Rogers
Inputter and Outputter okay
58
        self.test_data = {
59
            "scalars":{},
60
            "emr_spill_data":{},
61
            "spill_number":1,
340.3.18 by Chris Rogers
test_OutputCppRoot now works
62
            "run_number":1,
63
            "daq_event_type":"physics_event",
660.1.40 by Chris Rogers
Inputter and Outputter okay
64
            "recon_events":[],
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
65
            "mc_events":[],
663.9.111 by Chris Rogers
Looks reasonable, but having issues with the Inputter against semi-real data
66
            "maus_event_type":"Spill",
660.1.40 by Chris Rogers
Inputter and Outputter okay
67
        }
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
68
        self.test_job_header = {
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
69
                "start_of_job":{"date_time":"1976-04-04T00:00:00.000000"},
70
                "bzr_configuration":"",
71
                "bzr_revision":"",
72
                "bzr_status":"",
663.9.112 by Chris Rogers
JobFooter added
73
                "maus_version":"",
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
74
                "json_configuration":"output cpp root test",
663.9.111 by Chris Rogers
Looks reasonable, but having issues with the Inputter against semi-real data
75
                "maus_event_type":"JobHeader",
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
76
            }
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
77
        self.test_run_header = {
78
                "run_number":1,
79
                "maus_event_type":"RunHeader"
80
        }
81
        self.test_run_footer = {
82
                "run_number":-1,
83
                "maus_event_type":"RunFooter"
84
        }
85
        self.test_job_footer = {
663.9.112 by Chris Rogers
JobFooter added
86
                "end_of_job":{"date_time":"1977-04-04T00:00:00.000000"},
87
                "maus_event_type":"JobFooter",
88
            }
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
89
        self.cards = Configuration.Configuration().getConfigJSON()
90
        self.cards = json.loads(self.cards)
91
        self.cards["output_root_file_name"] = self.outfile
663.33.2 by Chris Rogers
Maybe passes tests
92
        self.cards["output_root_file_mode"] = "one_big_file"
93
        self.cards["end_of_run_output_root_directory"] = \
94
                                                      self.outdir+"/end_of_run/"
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
95
        self.cards["verbose_level"] = 2
96
        self.cards = json.dumps(self.cards)
97
        if maus_cpp.globals.has_instance():
98
            maus_cpp.globals.death()
99
        maus_cpp.globals.birth(self.cards)
100
        self.output.birth(self.cards)
663.33.2 by Chris Rogers
Maybe passes tests
101
        #ErrorHandler.DefaultHandler().on_error = 'raise'
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
102
103
    def tearDown(self): # pylint: disable=C0103, C0202
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
104
        """
105
        Destructor
106
        """
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
107
        ErrorHandler.DefaultHandler().on_error = self.on_error_standard
108
        self.output.death()
109
        maus_cpp.globals.death(self.cards)
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
110
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
111
    def _save_event(self, event):
112
        """
113
        Experimenting with nested functions - looks like this is making segv
114
        when output.save is nested
115
        """
116
        return self.output.save(event)
117
118
    def sub_function(self, event):
119
        """
120
        Experimenting with nested functions - looks like this is making segv
121
        """
122
        return self._save_event(event)
123
124
    def sub_function_2(self, event):
125
        """
126
        Experimenting with nested functions - looks like this is making segv
127
        """
128
        return self._save_event(event)
129
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
130
    def test_birth_death(self):
131
        """
132
        Check that we can birth and death properly
133
        """
134
        an_output = OutputCppRoot.OutputCppRoot()
663.33.2 by Chris Rogers
Maybe passes tests
135
        # for now this does NOT return an exception... looks like it is caught
136
        # by the API... set ErrorHandler.on_error to 'raise' and we raise a
137
        # std::exception killing the tests (oops)
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
138
        an_output.birth(json.dumps({}))
663.33.2 by Chris Rogers
Maybe passes tests
139
        an_output.birth(json.dumps({'output_root_file_name':'',
140
                                    'output_root_file_mode':'one_big_file',
141
                                    'end_of_run_output_root_directory':'tmp'}))
142
        an_output.birth(json.dumps({'output_root_file_name':'test.root',
143
                                    'output_root_file_mode':'',
144
                                    'end_of_run_output_root_directory':'tmp'}))
145
        an_output.birth(json.dumps({'output_root_file_name':'test.root',
146
                                    'output_root_file_mode':'one',
147
                                    'end_of_run_output_root_directory':'tmp'}))
148
        # this is okay (dir can just be '')
149
        an_output.birth(json.dumps({'output_root_file_name':'test.root',
150
                                    'output_root_file_mode':'one_big_file',
151
                                    'end_of_run_output_root_directory':''}))
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
152
        an_output.birth(self.cards)
153
        an_output.death()
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
154
155
    def test_save_normal_event(self):
156
        """
157
        Try saving a few standard events
158
        """
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
159
        self.assertTrue(self._save_event(
160
            json.dumps(self.test_data)
161
        ))
162
        self.assertTrue(self._save_event(
163
            json.dumps(self.test_data)
164
        ))
165
        self.assertTrue(self._save_event(
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
166
            json.dumps(self.test_data)
167
        ))
168
        self.output.death() # close the ROOT file
169
        root_file = ROOT.TFile(self.outfile, "READ") # pylint: disable = E1101
170
        data = ROOT.MAUS.Data() # pylint: disable = E1101
171
        tree = root_file.Get("Spill")
172
        tree.SetBranchAddress("data", data)
173
        self.assertEqual(tree.GetEntries(), 3)
174
        for i in range(tree.GetEntries()):
175
            tree.GetEntry(i)
176
            self.assertNotEqual(data.GetSpill().GetSpillNumber(), 0)
177
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
178
    def test_save_bad_event(self):
179
        """
180
        Check that if passed a bad event, code fails gracefully
181
        """
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
182
        self.assertFalse(self._save_event(json.dumps({"no_branch":{}})))
183
        self.assertFalse(self._save_event(''))
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
184
185
    def test_save_bad_job_header(self):
186
        """
187
        Check that if passed a bad header, code fails gracefully
188
        """
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
189
        self.assertFalse(self._save_event(json.dumps({"":{}})))
190
        self.assertFalse(self._save_event(''))
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
191
663.33.2 by Chris Rogers
Maybe passes tests
192
    def __check_job_header(self, json_conf_text, n_entries, entry, fname=None):
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
193
        """
194
        Check that json_header entry has json_conf_text 
195
        """
663.33.2 by Chris Rogers
Maybe passes tests
196
        if fname == None:
197
            fname = self.outfile
198
        root_file = ROOT.TFile(fname, "READ") # pylint: disable = E1101
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
199
        job_header = ROOT.MAUS.JobHeaderData() # pylint: disable = E1101
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
200
        tree = root_file.Get("JobHeader")
201
        tree.SetBranchAddress("job_header", job_header)
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
202
        self.assertEqual(tree.GetEntries(), n_entries)
203
        tree.GetEntry(entry)
204
        self.assertEqual(job_header.GetJobHeader().GetJsonConfiguration(),
205
                         json_conf_text)
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
206
        root_file.Close()
207
663.33.2 by Chris Rogers
Maybe passes tests
208
    def __check_job_footer(self, datetime_string, n_entries, entry, fname=None):
663.9.112 by Chris Rogers
JobFooter added
209
        """
210
        Check that json_footer entry has datetime_string
211
        """
663.33.2 by Chris Rogers
Maybe passes tests
212
        if fname == None:
213
            fname = self.outfile
214
        root_file = ROOT.TFile(fname, "READ") # pylint: disable = E1101
663.9.112 by Chris Rogers
JobFooter added
215
        job_footer = ROOT.MAUS.JobFooterData() # pylint: disable = E1101
216
        tree = root_file.Get("JobFooter")
217
        tree.SetBranchAddress("job_footer", job_footer)
218
        self.assertEqual(tree.GetEntries(), n_entries)
219
        tree.GetEntry(entry)
220
        self.assertEqual(job_footer.GetJobFooter().GetEndOfJob().GetDateTime(),
221
                         datetime_string)
222
        root_file.Close()
223
663.33.2 by Chris Rogers
Maybe passes tests
224
    def __check_run_header(self, run_number, n_entries, entry, fname=None):
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
225
        """
226
        Check that json_header entry has json_conf_text 
227
        """
663.33.2 by Chris Rogers
Maybe passes tests
228
        if fname == None:
229
            fname = self.outfile
230
        root_file = ROOT.TFile(fname, "READ") # pylint: disable = E1101
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
231
        run_header = ROOT.MAUS.RunHeaderData() # pylint: disable = E1101
232
        tree = root_file.Get("RunHeader")
233
        tree.SetBranchAddress("run_header", run_header)
234
        self.assertEqual(tree.GetEntries(), n_entries)
235
        tree.GetEntry(entry)
236
        self.assertEqual(run_header.GetRunHeader().GetRunNumber(),
237
                         run_number)
238
        root_file.Close()
239
663.9.109 by Chris Rogers
JobHeader into Output (but not yet into input)
240
    def test_save_normal_job_header(self):
241
        """
242
        Try saving a standard header; check we can only save job header once
243
        """
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
244
        self.assertTrue(self._save_event(
245
            json.dumps(self.test_job_header)
246
        ))
247
        self.test_job_header["json_configuration"] = "output cpp root test 2"
248
        self.assertTrue(self._save_event(
249
            json.dumps(self.test_job_header)
250
        ))
251
        self.output.death()
252
        self.__check_job_header("output cpp root test", 2, 0)
253
        self.__check_job_header("output cpp root test 2", 2, 1)
254
255
    def test_save_run_header(self):
256
        """
257
        test_OutputCppRoot: Try saving a run header
258
        """
259
        self.assertTrue(self.sub_function(
260
            json.dumps(self.test_run_header)
261
        ))
262
        self.test_run_header["run_number"] = 2
263
        for i in range(1000): # pylint:disable=W0612
264
            self.assertTrue(self.sub_function(
265
                json.dumps(self.test_run_header)
266
            ))
267
        self.test_run_header["run_number"] = 3
268
        self.sub_function_2(json.dumps(self.test_run_header))
269
        #self._save_event(json.dumps(self.test_run_header)) SEGMENTATION FAULT
270
        #self.output.save(json.dumps(self.test_run_header)) SEGMENTATION FAULT
271
        self.output.death()
272
        self.__check_run_header(1, 1002, 0)
273
        self.__check_run_header(2, 1002, 1000)
663.9.110 by Chris Rogers
JobHeader now works for the ROOT input and output (and is extensible in some reasonable way to other event types)
274
275
    def test_mixed_types(self):
276
        """
277
        test_OutputCppRoot.test_mixed_types check we can load alternating types
278
        """
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
279
        self.assertTrue(self._save_event(
280
            json.dumps(self.test_job_header)
281
        ))
282
        self.assertTrue(self._save_event(
283
            json.dumps(self.test_data)
284
        ))
285
        self.test_job_header["json_configuration"] = "output cpp root test 2"
286
        self.assertTrue(self._save_event(
287
            json.dumps(self.test_job_header)
288
        ))
289
        self.assertTrue(self._save_event(
290
            json.dumps(self.test_job_footer)
291
        ))
292
        self.assertTrue(self._save_event(
293
            json.dumps(self.test_data)
294
        ))
295
        self.assertTrue(self._save_event(
296
            json.dumps(self.test_job_footer)
663.9.112 by Chris Rogers
JobFooter added
297
        ))
298
        self.output.death()
663.9.130 by Chris Rogers
See if it builds - I failed to defeat the bug in OutputCppRoot...
299
        self.__check_job_header("output cpp root test", 2, 0)
300
        self.__check_job_header("output cpp root test 2", 2, 1)
301
        self.__check_job_footer("1977-04-04T00:00:00.000000", 2, 1)
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
302
663.33.2 by Chris Rogers
Maybe passes tests
303
    def test_one_file_per_run(self):
304
        """
305
        test_OutputCppRoot.test_one_file_per_run: one_file_per_run option
306
        """
307
        my_output = OutputCppRoot.OutputCppRoot()
308
        cards_py = json.loads(self.cards)
309
        cards_py["output_root_file_mode"] = "one_file_per_run"
310
        self.cards = json.dumps(cards_py)
311
        my_output.birth(self.cards)
312
        self.test_data["run_number"] = 10
313
        self.test_run_header["run_number"] = 10
314
        for i in range(3): # pylint: disable = W0612
315
            self.assertTrue(my_output.save(
316
                json.dumps(self.test_data)
317
            ))
318
        for i in range(2): # pylint: disable = W0612
319
            self.assertTrue(my_output.save(
320
                json.dumps(self.test_run_header)
321
            ))
322
        for i in range(3): # pylint: disable = W0612
323
            self.assertTrue(my_output.save(
324
                json.dumps(self.test_data)
325
            ))
326
        self.test_run_header["run_number"] = 9
327
        for i in range(3): # pylint: disable = W0612
328
            self.assertTrue(my_output.save(
329
                json.dumps(self.test_run_header)
330
            ))
331
        for i in range(3): # pylint: disable = W0612
332
            self.assertTrue(my_output.save(
333
                json.dumps(self.test_job_header)
334
            ))
335
        my_output.death()
336
        self.__check_job_header("output cpp root test", 3, 0,
337
                                        self.outdir+"test_outputCppRoot_0.root")
338
        self.__check_run_header(10, 2, 0,
339
                                       self.outdir+"test_outputCppRoot_10.root")
340
        self.__check_run_header(9, 3, 0,
341
                                        self.outdir+"test_outputCppRoot_9.root")
342
343
    def test_end_of_run_file_per_run(self):
344
        """
345
        test_OutputCppRoot.test_one_file_per_run: end_of_run_file_per_run option
346
        """
347
        my_output = OutputCppRoot.OutputCppRoot()
348
        cards_py = json.loads(self.cards)
349
        cards_py["output_root_file_mode"] = "end_of_run_file_per_run"
350
        cards_py["output_root_file_name"] = "test_outputCppRoot.root"
351
        cards_py["end_of_run_output_root_directory"] = \
352
                                                      self.outdir+"/end_of_run/"
353
        self.cards = json.dumps(cards_py)
354
        my_output.birth(self.cards)
355
        self.test_data["run_number"] = 10
356
        self.test_run_header["run_number"] = 10
357
        for i in range(3): # pylint: disable = W0612
358
            self.assertTrue(my_output.save(
359
                json.dumps(self.test_data)
360
            ))
361
        for i in range(2): # pylint: disable = W0612
362
            self.assertTrue(my_output.save(
363
                json.dumps(self.test_run_header)
364
            ))
365
        for i in range(3): # pylint: disable = W0612
366
            self.assertTrue(my_output.save(
367
                json.dumps(self.test_data)
368
            ))
369
        self.test_run_header["run_number"] = 9
370
        for i in range(3): # pylint: disable = W0612
371
            self.assertTrue(my_output.save(
372
                json.dumps(self.test_run_header)
373
            ))
374
        self.assertTrue(my_output.save(
375
            json.dumps(self.test_job_footer)
376
        ))
377
        self.test_run_footer["run_number"] = 9
378
        self.assertTrue(my_output.save(
379
            json.dumps(self.test_run_footer)
380
        ))
381
        for i in range(3): # pylint: disable = W0612
382
            self.assertTrue(my_output.save(
383
                json.dumps(self.test_job_header)
384
            ))
385
        my_output.death()
386
        self.__check_job_header("output cpp root test", 3, 0,
387
                           self.outdir+"/end_of_run/0/test_outputCppRoot.root")
388
        self.__check_run_header(10, 2, 0,
389
                           self.outdir+"/end_of_run/10/test_outputCppRoot.root")
390
        self.__check_run_header(9, 3, 0,
391
                           self.outdir+"/end_of_run/9/test_outputCppRoot.root")
392
        self.__check_job_footer("1977-04-04T00:00:00.000000", 1, 0,
393
                           self.outdir+"/end_of_run/0/test_outputCppRoot.root")
394
395
660.1.41 by Chris Rogers
test_root_io - throwing horrid segmentation faults etc and doesnt seem to be doing things right for root_to_json
396
if __name__ == "__main__":
660.1.8 by Chris Rogers
Wrote some tests of OutputCppRoot, some of which fail (just checking basic functionality)
397
    unittest.main()
398
399