Fix resource leak warned by Python 3
[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         with open(expectfile, encoding='utf-8') as f:
25             expect = f.readlines()
26         self.diff_report(cmd, expect, expectfile=expectfile)
27
28     def diff_report(self, cmd, expect, expectfile='(expected)'):
29         got = [x+"\n" for x in cmd.report().strip("\n").split("\n")]
30         self.assertEqual(got, expect, "\n"+"".join(difflib.context_diff(
31             expect, got, fromfile=expectfile, tofile="(generated)")))
32
33
34 class SummarizeFile(ReportDiff):
35     def test_example_files(self):
36         for fnm in glob.glob(os.path.join(TESTS_DIR, '*.txt.gz')):
37             logfile = os.path.join(TESTS_DIR, fnm)
38             args = crunchstat_summary.command.ArgumentParser().parse_args(
39                 ['--log-file', logfile])
40             cmd = crunchstat_summary.command.Command(args)
41             cmd.run()
42             self.diff_known_report(logfile, cmd)
43
44
45 class HTMLFromFile(ReportDiff):
46     def test_example_files(self):
47         # Note we don't test the output content at all yet; we're
48         # mainly just verifying the --format=html option isn't ignored
49         # and the HTML code path doesn't crash.
50         for fnm in glob.glob(os.path.join(TESTS_DIR, '*.txt.gz')):
51             logfile = os.path.join(TESTS_DIR, fnm)
52             args = crunchstat_summary.command.ArgumentParser().parse_args(
53                 ['--format=html', '--log-file', logfile])
54             cmd = crunchstat_summary.command.Command(args)
55             cmd.run()
56             self.assertRegexpMatches(cmd.report(), r'(?is)<html>.*</html>\s*$')
57
58 class SummarizeEdgeCases(unittest.TestCase):
59     def test_error_messages(self):
60         logfile = open(os.path.join(TESTS_DIR, 'crunchstat_error_messages.txt'), encoding='utf-8')
61         s = crunchstat_summary.summarizer.Summarizer(logfile)
62         s.run()
63
64
65 class SummarizeContainer(ReportDiff):
66     fake_container = {
67         'uuid': '9tee4-dz642-lymtndkpy39eibk',
68         'created_at': '2017-08-18T14:27:25.371388141',
69         'log': '9tee4-4zz18-ihyzym9tcwjwg4r',
70     }
71     fake_request = {
72         'uuid': '9tee4-xvhdp-uper95jktm10d3w',
73         'name': 'container',
74         'created_at': '2017-08-18T14:27:25.242339223Z',
75         'container_uuid': fake_container['uuid'],
76     }
77     reportfile = os.path.join(
78         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk.txt.gz')
79     logfile = os.path.join(
80         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk-crunchstat.txt.gz')
81     arvmountlog = os.path.join(
82         TESTS_DIR, 'container_9tee4-dz642-lymtndkpy39eibk-arv-mount.txt.gz')
83
84     @mock.patch('arvados.collection.CollectionReader')
85     @mock.patch('arvados.api')
86     def test_container(self, mock_api, mock_cr):
87         mock_api().container_requests().index().execute.return_value = {'items':[]}
88         mock_api().container_requests().get().execute.return_value = self.fake_request
89         mock_api().containers().get().execute.return_value = self.fake_container
90         mock_cr().__iter__.return_value = [
91             'crunch-run.txt', 'stderr.txt', 'node-info.txt',
92             'container.json', 'crunchstat.txt', 'arv-mount.txt']
93         def _open(n):
94             if n == "crunchstat.txt":
95                 return UTF8Decode(gzip.open(self.logfile))
96             elif n == "arv-mount.txt":
97                 return UTF8Decode(gzip.open(self.arvmountlog))
98         mock_cr().open.side_effect = _open
99         args = crunchstat_summary.command.ArgumentParser().parse_args(
100             ['--job', self.fake_request['uuid']])
101         cmd = crunchstat_summary.command.Command(args)
102         cmd.run()
103         self.diff_known_report(self.reportfile, cmd)
104
105
106 class SummarizeJob(ReportDiff):
107     fake_job_uuid = '4xphq-8i9sb-jq0ekny1xou3zoh'
108     fake_log_id = 'fake-log-collection-id'
109     fake_job = {
110         'uuid': fake_job_uuid,
111         'log': fake_log_id,
112     }
113     logfile = os.path.join(TESTS_DIR, 'logfile_20151204190335.txt.gz')
114
115     @mock.patch('arvados.collection.CollectionReader')
116     @mock.patch('arvados.api')
117     def test_job_report(self, mock_api, mock_cr):
118         mock_api().jobs().get().execute.return_value = self.fake_job
119         mock_cr().__iter__.return_value = ['fake-logfile.txt']
120         mock_cr().open.return_value = UTF8Decode(gzip.open(self.logfile))
121         args = crunchstat_summary.command.ArgumentParser().parse_args(
122             ['--job', self.fake_job_uuid])
123         cmd = crunchstat_summary.command.Command(args)
124         cmd.run()
125         self.diff_known_report(self.logfile, cmd)
126         mock_api().jobs().get.assert_called_with(uuid=self.fake_job_uuid)
127         mock_cr.assert_called_with(self.fake_log_id)
128         mock_cr().open.assert_called_with('fake-logfile.txt')
129
130
131 class SummarizePipeline(ReportDiff):
132     fake_instance = {
133         'uuid': 'zzzzz-d1hrv-i3e77t9z5y8j9cc',
134         'owner_uuid': 'zzzzz-tpzed-xurymjxw79nv3jz',
135         'components': collections.OrderedDict([
136             ['foo', {
137                 'job': {
138                     'uuid': 'zzzzz-8i9sb-000000000000000',
139                     'log': 'fake-log-pdh-0',
140                     'runtime_constraints': {
141                         'min_ram_mb_per_node': 900,
142                         'min_cores_per_node': 1,
143                     },
144                 },
145             }],
146             ['bar', {
147                 'job': {
148                     'uuid': 'zzzzz-8i9sb-000000000000001',
149                     'log': 'fake-log-pdh-1',
150                     'runtime_constraints': {
151                         'min_ram_mb_per_node': 900,
152                         'min_cores_per_node': 1,
153                     },
154                 },
155             }],
156             ['no-job-assigned', {}],
157             ['unfinished-job', {
158                 'job': {
159                     'uuid': 'zzzzz-8i9sb-xxxxxxxxxxxxxxx',
160                 },
161             }],
162             ['baz', {
163                 'job': {
164                     'uuid': 'zzzzz-8i9sb-000000000000002',
165                     'log': 'fake-log-pdh-2',
166                     'runtime_constraints': {
167                         'min_ram_mb_per_node': 900,
168                         'min_cores_per_node': 1,
169                     },
170                 },
171             }]]),
172     }
173
174     @mock.patch('arvados.collection.CollectionReader')
175     @mock.patch('arvados.api')
176     def test_pipeline(self, mock_api, mock_cr):
177         logfile = os.path.join(TESTS_DIR, 'logfile_20151204190335.txt.gz')
178         mock_api().pipeline_instances().get().execute. \
179             return_value = self.fake_instance
180         mock_cr().__iter__.return_value = ['fake-logfile.txt']
181         mock_cr().open.side_effect = [UTF8Decode(gzip.open(logfile)) for _ in range(3)]
182         args = crunchstat_summary.command.ArgumentParser().parse_args(
183             ['--pipeline-instance', self.fake_instance['uuid']])
184         cmd = crunchstat_summary.command.Command(args)
185         cmd.run()
186
187         with open(logfile+'.report', encoding='utf-8') as f:
188             job_report = [line for line in f 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         with open(logfile+'.report', encoding='utf-8') as f:
263             job_report = [line for line in f if not line.startswith('#!! ')]
264         expect = (
265             ['### Summary for zzzzz-8i9sb-i3e77t9z5y8j9cc (partial) (zzzzz-8i9sb-i3e77t9z5y8j9cc)\n',
266              '(no report generated)\n',
267              '\n'] +
268             ['### Summary for bar (zzzzz-8i9sb-000000000000001)\n'] +
269             job_report + ['\n'] +
270             ['### Summary for baz (zzzzz-8i9sb-000000000000002)\n'] +
271             job_report + ['\n'] +
272             ['### Summary for foo (zzzzz-8i9sb-000000000000000)\n'] +
273             job_report + ['\n'] +
274             ['### Summary for unfinished-job (partial) (zzzzz-8i9sb-xxxxxxxxxxxxxxx)\n',
275              '(no report generated)\n']
276         )
277         self.diff_report(cmd, expect)
278         mock_cr.assert_has_calls(
279             [
280                 mock.call('fake-log-pdh-0'),
281                 mock.call('fake-log-pdh-1'),
282                 mock.call('fake-log-pdh-2'),
283             ], any_order=True)
284         mock_cr().open.assert_called_with('fake-logfile.txt')