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