af92becd80a6875d64e1d406d2b21f8bfbd6ec57
[arvados.git] / tools / crunchstat-summary / tests / test_examples.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 import arvados
6 import collections
7 import crunchstat_summary.command
8 import difflib
9 import glob
10 import gzip
11 import mock
12 import os
13 import unittest
14
15 TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
16
17
18 class ReportDiff(unittest.TestCase):
19     def diff_known_report(self, logfile, cmd):
20         expectfile = logfile+'.report'
21         expect = open(expectfile).readlines()
22         self.diff_report(cmd, expect, expectfile=expectfile)
23
24     def diff_report(self, cmd, expect, expectfile=None):
25         got = [x+"\n" for x in cmd.report().strip("\n").split("\n")]
26         self.assertEqual(got, expect, "\n"+"".join(difflib.context_diff(
27             expect, got, fromfile=expectfile, tofile="(generated)")))
28
29
30 class SummarizeFile(ReportDiff):
31     def test_example_files(self):
32         for fnm in glob.glob(os.path.join(TESTS_DIR, '*.txt.gz')):
33             logfile = os.path.join(TESTS_DIR, fnm)
34             args = crunchstat_summary.command.ArgumentParser().parse_args(
35                 ['--log-file', logfile])
36             cmd = crunchstat_summary.command.Command(args)
37             cmd.run()
38             self.diff_known_report(logfile, cmd)
39
40
41 class HTMLFromFile(ReportDiff):
42     def test_example_files(self):
43         # Note we don't test the output content at all yet; we're
44         # mainly just verifying the --format=html option isn't ignored
45         # and the HTML code path doesn't crash.
46         for fnm in glob.glob(os.path.join(TESTS_DIR, '*.txt.gz')):
47             logfile = os.path.join(TESTS_DIR, fnm)
48             args = crunchstat_summary.command.ArgumentParser().parse_args(
49                 ['--format=html', '--log-file', logfile])
50             cmd = crunchstat_summary.command.Command(args)
51             cmd.run()
52             self.assertRegexpMatches(cmd.report(), r'(?is)<html>.*</html>\s*$')
53
54
55 class SummarizeEdgeCases(unittest.TestCase):
56     def test_error_messages(self):
57         logfile = open(os.path.join(TESTS_DIR, 'crunchstat_error_messages.txt'))
58         s = crunchstat_summary.summarizer.Summarizer(logfile)
59         s.run()
60
61
62 class SummarizeContainer(ReportDiff):
63     fake_container = {
64         'uuid': '9tee4-dz642-lymtndkpy39eibk',
65         'created_at': '2017-08-18T14:27:25.371388141',
66         'log': '9tee4-4zz18-ihyzym9tcwjwg4r',
67     }
68     fake_request = {
69         'uuid': '9tee4-xvhdp-uper95jktm10d3w',
70         'name': 'container',
71         'created_at': '2017-08-18T14:27:25.242339223Z',
72         'container_uuid': fake_container['uuid'],
73     }
74     reportfile = os.path.join(
75         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk.txt.gz')
76     logfile = os.path.join(
77         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk-crunchstat.txt.gz')
78     arvmountlog = os.path.join(
79         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk-arv-mount.txt.gz')
80
81     @mock.patch('arvados.collection.CollectionReader')
82     @mock.patch('arvados.api')
83     def test_container(self, mock_api, mock_cr):
84         mock_api().container_requests().index().execute.return_value = {'items':[]}
85         mock_api().container_requests().get().execute.return_value = self.fake_request
86         mock_api().containers().get().execute.return_value = self.fake_container
87         mock_cr().__iter__.return_value = [
88             'crunch-run.txt', 'stderr.txt', 'node-info.txt',
89             'container.json', 'crunchstat.txt', 'arv-mount.txt']
90         def _open(n):
91             if n == "crunchstat.txt":
92                 return gzip.open(self.logfile)
93             elif n == "arv-mount.txt":
94                 return gzip.open(self.arvmountlog)
95         mock_cr().open.side_effect = _open
96         args = crunchstat_summary.command.ArgumentParser().parse_args(
97             ['--job', self.fake_request['uuid']])
98         cmd = crunchstat_summary.command.Command(args)
99         cmd.run()
100         self.diff_known_report(self.reportfile, cmd)
101
102
103 class SummarizeJob(ReportDiff):
104     fake_job_uuid = '4xphq-8i9sb-jq0ekny1xou3zoh'
105     fake_log_id = 'fake-log-collection-id'
106     fake_job = {
107         'uuid': fake_job_uuid,
108         'log': fake_log_id,
109     }
110     logfile = os.path.join(TESTS_DIR, 'logfile_20151204190335.txt.gz')
111
112     @mock.patch('arvados.collection.CollectionReader')
113     @mock.patch('arvados.api')
114     def test_job_report(self, mock_api, mock_cr):
115         mock_api().jobs().get().execute.return_value = self.fake_job
116         mock_cr().__iter__.return_value = ['fake-logfile.txt']
117         mock_cr().open.return_value = gzip.open(self.logfile)
118         args = crunchstat_summary.command.ArgumentParser().parse_args(
119             ['--job', self.fake_job_uuid])
120         cmd = crunchstat_summary.command.Command(args)
121         cmd.run()
122         self.diff_known_report(self.logfile, cmd)
123         mock_api().jobs().get.assert_called_with(uuid=self.fake_job_uuid)
124         mock_cr.assert_called_with(self.fake_log_id)
125         mock_cr().open.assert_called_with('fake-logfile.txt')
126
127
128 class SummarizePipeline(ReportDiff):
129     fake_instance = {
130         'uuid': 'zzzzz-d1hrv-i3e77t9z5y8j9cc',
131         'owner_uuid': 'zzzzz-tpzed-xurymjxw79nv3jz',
132         'components': collections.OrderedDict([
133             ['foo', {
134                 'job': {
135                     'uuid': 'zzzzz-8i9sb-000000000000000',
136                     'log': 'fake-log-pdh-0',
137                     'runtime_constraints': {
138                         'min_ram_mb_per_node': 900,
139                         'min_cores_per_node': 1,
140                     },
141                 },
142             }],
143             ['bar', {
144                 'job': {
145                     'uuid': 'zzzzz-8i9sb-000000000000001',
146                     'log': 'fake-log-pdh-1',
147                     'runtime_constraints': {
148                         'min_ram_mb_per_node': 900,
149                         'min_cores_per_node': 1,
150                     },
151                 },
152             }],
153             ['no-job-assigned', {}],
154             ['unfinished-job', {
155                 'job': {
156                     'uuid': 'zzzzz-8i9sb-xxxxxxxxxxxxxxx',
157                 },
158             }],
159             ['baz', {
160                 'job': {
161                     'uuid': 'zzzzz-8i9sb-000000000000002',
162                     'log': 'fake-log-pdh-2',
163                     'runtime_constraints': {
164                         'min_ram_mb_per_node': 900,
165                         'min_cores_per_node': 1,
166                     },
167                 },
168             }]]),
169     }
170
171     @mock.patch('arvados.collection.CollectionReader')
172     @mock.patch('arvados.api')
173     def test_pipeline(self, mock_api, mock_cr):
174         logfile = os.path.join(TESTS_DIR, 'logfile_20151204190335.txt.gz')
175         mock_api().pipeline_instances().get().execute. \
176             return_value = self.fake_instance
177         mock_cr().__iter__.return_value = ['fake-logfile.txt']
178         mock_cr().open.side_effect = [gzip.open(logfile) for _ in range(3)]
179         args = crunchstat_summary.command.ArgumentParser().parse_args(
180             ['--pipeline-instance', self.fake_instance['uuid']])
181         cmd = crunchstat_summary.command.Command(args)
182         cmd.run()
183
184         job_report = [
185             line for line in open(logfile+'.report').readlines()
186             if not line.startswith('#!! ')]
187         expect = (
188             ['### Summary for foo (zzzzz-8i9sb-000000000000000)\n'] +
189             job_report + ['\n'] +
190             ['### Summary for bar (zzzzz-8i9sb-000000000000001)\n'] +
191             job_report + ['\n'] +
192             ['### Summary for unfinished-job (partial) (zzzzz-8i9sb-xxxxxxxxxxxxxxx)\n',
193              '(no report generated)\n',
194              '\n'] +
195             ['### Summary for baz (zzzzz-8i9sb-000000000000002)\n'] +
196             job_report)
197         self.diff_report(cmd, expect)
198         mock_cr.assert_has_calls(
199             [
200                 mock.call('fake-log-pdh-0'),
201                 mock.call('fake-log-pdh-1'),
202                 mock.call('fake-log-pdh-2'),
203             ], any_order=True)
204         mock_cr().open.assert_called_with('fake-logfile.txt')
205
206
207 class SummarizeACRJob(ReportDiff):
208     fake_job = {
209         'uuid': 'zzzzz-8i9sb-i3e77t9z5y8j9cc',
210         'owner_uuid': 'zzzzz-tpzed-xurymjxw79nv3jz',
211         'components': {
212             'foo': 'zzzzz-8i9sb-000000000000000',
213             'bar': 'zzzzz-8i9sb-000000000000001',
214             'unfinished-job': 'zzzzz-8i9sb-xxxxxxxxxxxxxxx',
215             'baz': 'zzzzz-8i9sb-000000000000002',
216         }
217     }
218     fake_jobs_index = { 'items': [
219         {
220             'uuid': 'zzzzz-8i9sb-000000000000000',
221             'log': 'fake-log-pdh-0',
222             'runtime_constraints': {
223                 'min_ram_mb_per_node': 900,
224                 'min_cores_per_node': 1,
225             },
226         },
227         {
228             'uuid': 'zzzzz-8i9sb-000000000000001',
229             'log': 'fake-log-pdh-1',
230             'runtime_constraints': {
231                 'min_ram_mb_per_node': 900,
232                 'min_cores_per_node': 1,
233             },
234         },
235         {
236             'uuid': 'zzzzz-8i9sb-xxxxxxxxxxxxxxx',
237         },
238         {
239             'uuid': 'zzzzz-8i9sb-000000000000002',
240             'log': 'fake-log-pdh-2',
241             'runtime_constraints': {
242                 'min_ram_mb_per_node': 900,
243                 'min_cores_per_node': 1,
244             },
245         },
246     ]}
247     @mock.patch('arvados.collection.CollectionReader')
248     @mock.patch('arvados.api')
249     def test_acr_job(self, mock_api, mock_cr):
250         logfile = os.path.join(TESTS_DIR, 'logfile_20151204190335.txt.gz')
251         mock_api().jobs().index().execute.return_value = self.fake_jobs_index
252         mock_api().jobs().get().execute.return_value = self.fake_job
253         mock_cr().__iter__.return_value = ['fake-logfile.txt']
254         mock_cr().open.side_effect = [gzip.open(logfile) for _ in range(3)]
255         args = crunchstat_summary.command.ArgumentParser().parse_args(
256             ['--job', self.fake_job['uuid']])
257         cmd = crunchstat_summary.command.Command(args)
258         cmd.run()
259
260         job_report = [
261             line for line in open(logfile+'.report').readlines()
262             if not line.startswith('#!! ')]
263         expect = (
264             ['### Summary for zzzzz-8i9sb-i3e77t9z5y8j9cc (partial) (zzzzz-8i9sb-i3e77t9z5y8j9cc)\n',
265              '(no report generated)\n',
266              '\n'] +
267             ['### Summary for bar (zzzzz-8i9sb-000000000000001)\n'] +
268             job_report + ['\n'] +
269             ['### Summary for baz (zzzzz-8i9sb-000000000000002)\n'] +
270             job_report + ['\n'] +
271             ['### Summary for foo (zzzzz-8i9sb-000000000000000)\n'] +
272             job_report + ['\n'] +
273             ['### Summary for unfinished-job (partial) (zzzzz-8i9sb-xxxxxxxxxxxxxxx)\n',
274              '(no report generated)\n']
275         )
276         self.diff_report(cmd, expect)
277         mock_cr.assert_has_calls(
278             [
279                 mock.call('fake-log-pdh-0'),
280                 mock.call('fake-log-pdh-1'),
281                 mock.call('fake-log-pdh-2'),
282             ], any_order=True)
283         mock_cr().open.assert_called_with('fake-logfile.txt')