summaryrefslogtreecommitdiffstats
path: root/tests/test_git_trace2_event_log.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_git_trace2_event_log.py')
-rw-r--r--tests/test_git_trace2_event_log.py329
1 files changed, 329 insertions, 0 deletions
diff --git a/tests/test_git_trace2_event_log.py b/tests/test_git_trace2_event_log.py
new file mode 100644
index 00000000..89dcfb92
--- /dev/null
+++ b/tests/test_git_trace2_event_log.py
@@ -0,0 +1,329 @@
1# Copyright (C) 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Unittests for the git_trace2_event_log.py module."""
16
17import json
18import os
19import tempfile
20import unittest
21from unittest import mock
22
23import git_trace2_event_log
24
25
26class EventLogTestCase(unittest.TestCase):
27 """TestCase for the EventLog module."""
28
29 PARENT_SID_KEY = 'GIT_TRACE2_PARENT_SID'
30 PARENT_SID_VALUE = 'parent_sid'
31 SELF_SID_REGEX = r'repo-\d+T\d+Z-.*'
32 FULL_SID_REGEX = r'^%s/%s' % (PARENT_SID_VALUE, SELF_SID_REGEX)
33
34 def setUp(self):
35 """Load the event_log module every time."""
36 self._event_log_module = None
37 # By default we initialize with the expected case where
38 # repo launches us (so GIT_TRACE2_PARENT_SID is set).
39 env = {
40 self.PARENT_SID_KEY: self.PARENT_SID_VALUE,
41 }
42 self._event_log_module = git_trace2_event_log.EventLog(env=env)
43 self._log_data = None
44
45 def verifyCommonKeys(self, log_entry, expected_event_name=None, full_sid=True):
46 """Helper function to verify common event log keys."""
47 self.assertIn('event', log_entry)
48 self.assertIn('sid', log_entry)
49 self.assertIn('thread', log_entry)
50 self.assertIn('time', log_entry)
51
52 # Do basic data format validation.
53 if expected_event_name:
54 self.assertEqual(expected_event_name, log_entry['event'])
55 if full_sid:
56 self.assertRegex(log_entry['sid'], self.FULL_SID_REGEX)
57 else:
58 self.assertRegex(log_entry['sid'], self.SELF_SID_REGEX)
59 self.assertRegex(log_entry['time'], r'^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$')
60
61 def readLog(self, log_path):
62 """Helper function to read log data into a list."""
63 log_data = []
64 with open(log_path, mode='rb') as f:
65 for line in f:
66 log_data.append(json.loads(line))
67 return log_data
68
69 def remove_prefix(self, s, prefix):
70 """Return a copy string after removing |prefix| from |s|, if present or the original string."""
71 if s.startswith(prefix):
72 return s[len(prefix):]
73 else:
74 return s
75
76 def test_initial_state_with_parent_sid(self):
77 """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent."""
78 self.assertRegex(self._event_log_module.full_sid, self.FULL_SID_REGEX)
79
80 def test_initial_state_no_parent_sid(self):
81 """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set."""
82 # Setup an empty environment dict (no parent sid).
83 self._event_log_module = git_trace2_event_log.EventLog(env={})
84 self.assertRegex(self._event_log_module.full_sid, self.SELF_SID_REGEX)
85
86 def test_version_event(self):
87 """Test 'version' event data is valid.
88
89 Verify that the 'version' event is written even when no other
90 events are addded.
91
92 Expected event log:
93 <version event>
94 """
95 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
96 log_path = self._event_log_module.Write(path=tempdir)
97 self._log_data = self.readLog(log_path)
98
99 # A log with no added events should only have the version entry.
100 self.assertEqual(len(self._log_data), 1)
101 version_event = self._log_data[0]
102 self.verifyCommonKeys(version_event, expected_event_name='version')
103 # Check for 'version' event specific fields.
104 self.assertIn('evt', version_event)
105 self.assertIn('exe', version_event)
106 # Verify "evt" version field is a string.
107 self.assertIsInstance(version_event['evt'], str)
108
109 def test_start_event(self):
110 """Test and validate 'start' event data is valid.
111
112 Expected event log:
113 <version event>
114 <start event>
115 """
116 self._event_log_module.StartEvent()
117 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
118 log_path = self._event_log_module.Write(path=tempdir)
119 self._log_data = self.readLog(log_path)
120
121 self.assertEqual(len(self._log_data), 2)
122 start_event = self._log_data[1]
123 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
124 self.verifyCommonKeys(start_event, expected_event_name='start')
125 # Check for 'start' event specific fields.
126 self.assertIn('argv', start_event)
127 self.assertTrue(isinstance(start_event['argv'], list))
128
129 def test_exit_event_result_none(self):
130 """Test 'exit' event data is valid when result is None.
131
132 We expect None result to be converted to 0 in the exit event data.
133
134 Expected event log:
135 <version event>
136 <exit event>
137 """
138 self._event_log_module.ExitEvent(None)
139 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
140 log_path = self._event_log_module.Write(path=tempdir)
141 self._log_data = self.readLog(log_path)
142
143 self.assertEqual(len(self._log_data), 2)
144 exit_event = self._log_data[1]
145 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
146 self.verifyCommonKeys(exit_event, expected_event_name='exit')
147 # Check for 'exit' event specific fields.
148 self.assertIn('code', exit_event)
149 # 'None' result should convert to 0 (successful) return code.
150 self.assertEqual(exit_event['code'], 0)
151
152 def test_exit_event_result_integer(self):
153 """Test 'exit' event data is valid when result is an integer.
154
155 Expected event log:
156 <version event>
157 <exit event>
158 """
159 self._event_log_module.ExitEvent(2)
160 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
161 log_path = self._event_log_module.Write(path=tempdir)
162 self._log_data = self.readLog(log_path)
163
164 self.assertEqual(len(self._log_data), 2)
165 exit_event = self._log_data[1]
166 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
167 self.verifyCommonKeys(exit_event, expected_event_name='exit')
168 # Check for 'exit' event specific fields.
169 self.assertIn('code', exit_event)
170 self.assertEqual(exit_event['code'], 2)
171
172 def test_command_event(self):
173 """Test and validate 'command' event data is valid.
174
175 Expected event log:
176 <version event>
177 <command event>
178 """
179 name = 'repo'
180 subcommands = ['init' 'this']
181 self._event_log_module.CommandEvent(name='repo', subcommands=subcommands)
182 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
183 log_path = self._event_log_module.Write(path=tempdir)
184 self._log_data = self.readLog(log_path)
185
186 self.assertEqual(len(self._log_data), 2)
187 command_event = self._log_data[1]
188 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
189 self.verifyCommonKeys(command_event, expected_event_name='command')
190 # Check for 'command' event specific fields.
191 self.assertIn('name', command_event)
192 self.assertIn('subcommands', command_event)
193 self.assertEqual(command_event['name'], name)
194 self.assertEqual(command_event['subcommands'], subcommands)
195
196 def test_def_params_event_repo_config(self):
197 """Test 'def_params' event data outputs only repo config keys.
198
199 Expected event log:
200 <version event>
201 <def_param event>
202 <def_param event>
203 """
204 config = {
205 'git.foo': 'bar',
206 'repo.partialclone': 'true',
207 'repo.partialclonefilter': 'blob:none',
208 }
209 self._event_log_module.DefParamRepoEvents(config)
210
211 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
212 log_path = self._event_log_module.Write(path=tempdir)
213 self._log_data = self.readLog(log_path)
214
215 self.assertEqual(len(self._log_data), 3)
216 def_param_events = self._log_data[1:]
217 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
218
219 for event in def_param_events:
220 self.verifyCommonKeys(event, expected_event_name='def_param')
221 # Check for 'def_param' event specific fields.
222 self.assertIn('param', event)
223 self.assertIn('value', event)
224 self.assertTrue(event['param'].startswith('repo.'))
225
226 def test_def_params_event_no_repo_config(self):
227 """Test 'def_params' event data won't output non-repo config keys.
228
229 Expected event log:
230 <version event>
231 """
232 config = {
233 'git.foo': 'bar',
234 'git.core.foo2': 'baz',
235 }
236 self._event_log_module.DefParamRepoEvents(config)
237
238 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
239 log_path = self._event_log_module.Write(path=tempdir)
240 self._log_data = self.readLog(log_path)
241
242 self.assertEqual(len(self._log_data), 1)
243 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
244
245 def test_data_event_config(self):
246 """Test 'data' event data outputs all config keys.
247
248 Expected event log:
249 <version event>
250 <data event>
251 <data event>
252 """
253 config = {
254 'git.foo': 'bar',
255 'repo.partialclone': 'false',
256 'repo.syncstate.superproject.hassuperprojecttag': 'true',
257 'repo.syncstate.superproject.sys.argv': ['--', 'sync', 'protobuf'],
258 }
259 prefix_value = 'prefix'
260 self._event_log_module.LogDataConfigEvents(config, prefix_value)
261
262 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
263 log_path = self._event_log_module.Write(path=tempdir)
264 self._log_data = self.readLog(log_path)
265
266 self.assertEqual(len(self._log_data), 5)
267 data_events = self._log_data[1:]
268 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
269
270 for event in data_events:
271 self.verifyCommonKeys(event)
272 # Check for 'data' event specific fields.
273 self.assertIn('key', event)
274 self.assertIn('value', event)
275 key = event['key']
276 key = self.remove_prefix(key, f'{prefix_value}/')
277 value = event['value']
278 self.assertEqual(self._event_log_module.GetDataEventName(value), event['event'])
279 self.assertTrue(key in config and value == config[key])
280
281 def test_error_event(self):
282 """Test and validate 'error' event data is valid.
283
284 Expected event log:
285 <version event>
286 <error event>
287 """
288 msg = 'invalid option: --cahced'
289 fmt = 'invalid option: %s'
290 self._event_log_module.ErrorEvent(msg, fmt)
291 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
292 log_path = self._event_log_module.Write(path=tempdir)
293 self._log_data = self.readLog(log_path)
294
295 self.assertEqual(len(self._log_data), 2)
296 error_event = self._log_data[1]
297 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
298 self.verifyCommonKeys(error_event, expected_event_name='error')
299 # Check for 'error' event specific fields.
300 self.assertIn('msg', error_event)
301 self.assertIn('fmt', error_event)
302 self.assertEqual(error_event['msg'], msg)
303 self.assertEqual(error_event['fmt'], fmt)
304
305 def test_write_with_filename(self):
306 """Test Write() with a path to a file exits with None."""
307 self.assertIsNone(self._event_log_module.Write(path='path/to/file'))
308
309 def test_write_with_git_config(self):
310 """Test Write() uses the git config path when 'git config' call succeeds."""
311 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
312 with mock.patch.object(self._event_log_module,
313 '_GetEventTargetPath', return_value=tempdir):
314 self.assertEqual(os.path.dirname(self._event_log_module.Write()), tempdir)
315
316 def test_write_no_git_config(self):
317 """Test Write() with no git config variable present exits with None."""
318 with mock.patch.object(self._event_log_module,
319 '_GetEventTargetPath', return_value=None):
320 self.assertIsNone(self._event_log_module.Write())
321
322 def test_write_non_string(self):
323 """Test Write() with non-string type for |path| throws TypeError."""
324 with self.assertRaises(TypeError):
325 self._event_log_module.Write(path=1234)
326
327
328if __name__ == '__main__':
329 unittest.main()