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.py725
1 files changed, 373 insertions, 352 deletions
diff --git a/tests/test_git_trace2_event_log.py b/tests/test_git_trace2_event_log.py
index 7e7dfb7a..a6078d38 100644
--- a/tests/test_git_trace2_event_log.py
+++ b/tests/test_git_trace2_event_log.py
@@ -27,361 +27,382 @@ import platform_utils
27 27
28 28
29def serverLoggingThread(socket_path, server_ready, received_traces): 29def serverLoggingThread(socket_path, server_ready, received_traces):
30 """Helper function to receive logs over a Unix domain socket. 30 """Helper function to receive logs over a Unix domain socket.
31
32 Appends received messages on the provided socket and appends to received_traces.
33
34 Args:
35 socket_path: path to a Unix domain socket on which to listen for traces
36 server_ready: a threading.Condition used to signal to the caller that this thread is ready to
37 accept connections
38 received_traces: a list to which received traces will be appended (after decoding to a utf-8
39 string).
40 """
41 platform_utils.remove(socket_path, missing_ok=True)
42 data = b''
43 with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
44 sock.bind(socket_path)
45 sock.listen(0)
46 with server_ready:
47 server_ready.notify()
48 with sock.accept()[0] as conn:
49 while True:
50 recved = conn.recv(4096)
51 if not recved:
52 break
53 data += recved
54 received_traces.extend(data.decode('utf-8').splitlines())
55 31
32 Appends received messages on the provided socket and appends to
33 received_traces.
56 34
57class EventLogTestCase(unittest.TestCase): 35 Args:
58 """TestCase for the EventLog module.""" 36 socket_path: path to a Unix domain socket on which to listen for traces
59 37 server_ready: a threading.Condition used to signal to the caller that
60 PARENT_SID_KEY = 'GIT_TRACE2_PARENT_SID' 38 this thread is ready to accept connections
61 PARENT_SID_VALUE = 'parent_sid' 39 received_traces: a list to which received traces will be appended (after
62 SELF_SID_REGEX = r'repo-\d+T\d+Z-.*' 40 decoding to a utf-8 string).
63 FULL_SID_REGEX = r'^%s/%s' % (PARENT_SID_VALUE, SELF_SID_REGEX)
64
65 def setUp(self):
66 """Load the event_log module every time."""
67 self._event_log_module = None
68 # By default we initialize with the expected case where
69 # repo launches us (so GIT_TRACE2_PARENT_SID is set).
70 env = {
71 self.PARENT_SID_KEY: self.PARENT_SID_VALUE,
72 }
73 self._event_log_module = git_trace2_event_log.EventLog(env=env)
74 self._log_data = None
75
76 def verifyCommonKeys(self, log_entry, expected_event_name=None, full_sid=True):
77 """Helper function to verify common event log keys."""
78 self.assertIn('event', log_entry)
79 self.assertIn('sid', log_entry)
80 self.assertIn('thread', log_entry)
81 self.assertIn('time', log_entry)
82
83 # Do basic data format validation.
84 if expected_event_name:
85 self.assertEqual(expected_event_name, log_entry['event'])
86 if full_sid:
87 self.assertRegex(log_entry['sid'], self.FULL_SID_REGEX)
88 else:
89 self.assertRegex(log_entry['sid'], self.SELF_SID_REGEX)
90 self.assertRegex(log_entry['time'], r'^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$')
91
92 def readLog(self, log_path):
93 """Helper function to read log data into a list."""
94 log_data = []
95 with open(log_path, mode='rb') as f:
96 for line in f:
97 log_data.append(json.loads(line))
98 return log_data
99
100 def remove_prefix(self, s, prefix):
101 """Return a copy string after removing |prefix| from |s|, if present or the original string."""
102 if s.startswith(prefix):
103 return s[len(prefix):]
104 else:
105 return s
106
107 def test_initial_state_with_parent_sid(self):
108 """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent."""
109 self.assertRegex(self._event_log_module.full_sid, self.FULL_SID_REGEX)
110
111 def test_initial_state_no_parent_sid(self):
112 """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set."""
113 # Setup an empty environment dict (no parent sid).
114 self._event_log_module = git_trace2_event_log.EventLog(env={})
115 self.assertRegex(self._event_log_module.full_sid, self.SELF_SID_REGEX)
116
117 def test_version_event(self):
118 """Test 'version' event data is valid.
119
120 Verify that the 'version' event is written even when no other
121 events are addded.
122
123 Expected event log:
124 <version event>
125 """
126 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
127 log_path = self._event_log_module.Write(path=tempdir)
128 self._log_data = self.readLog(log_path)
129
130 # A log with no added events should only have the version entry.
131 self.assertEqual(len(self._log_data), 1)
132 version_event = self._log_data[0]
133 self.verifyCommonKeys(version_event, expected_event_name='version')
134 # Check for 'version' event specific fields.
135 self.assertIn('evt', version_event)
136 self.assertIn('exe', version_event)
137 # Verify "evt" version field is a string.
138 self.assertIsInstance(version_event['evt'], str)
139
140 def test_start_event(self):
141 """Test and validate 'start' event data is valid.
142
143 Expected event log:
144 <version event>
145 <start event>
146 """
147 self._event_log_module.StartEvent()
148 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
149 log_path = self._event_log_module.Write(path=tempdir)
150 self._log_data = self.readLog(log_path)
151
152 self.assertEqual(len(self._log_data), 2)
153 start_event = self._log_data[1]
154 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
155 self.verifyCommonKeys(start_event, expected_event_name='start')
156 # Check for 'start' event specific fields.
157 self.assertIn('argv', start_event)
158 self.assertTrue(isinstance(start_event['argv'], list))
159
160 def test_exit_event_result_none(self):
161 """Test 'exit' event data is valid when result is None.
162
163 We expect None result to be converted to 0 in the exit event data.
164
165 Expected event log:
166 <version event>
167 <exit event>
168 """
169 self._event_log_module.ExitEvent(None)
170 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
171 log_path = self._event_log_module.Write(path=tempdir)
172 self._log_data = self.readLog(log_path)
173
174 self.assertEqual(len(self._log_data), 2)
175 exit_event = self._log_data[1]
176 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
177 self.verifyCommonKeys(exit_event, expected_event_name='exit')
178 # Check for 'exit' event specific fields.
179 self.assertIn('code', exit_event)
180 # 'None' result should convert to 0 (successful) return code.
181 self.assertEqual(exit_event['code'], 0)
182
183 def test_exit_event_result_integer(self):
184 """Test 'exit' event data is valid when result is an integer.
185
186 Expected event log:
187 <version event>
188 <exit event>
189 """
190 self._event_log_module.ExitEvent(2)
191 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
192 log_path = self._event_log_module.Write(path=tempdir)
193 self._log_data = self.readLog(log_path)
194
195 self.assertEqual(len(self._log_data), 2)
196 exit_event = self._log_data[1]
197 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
198 self.verifyCommonKeys(exit_event, expected_event_name='exit')
199 # Check for 'exit' event specific fields.
200 self.assertIn('code', exit_event)
201 self.assertEqual(exit_event['code'], 2)
202
203 def test_command_event(self):
204 """Test and validate 'command' event data is valid.
205
206 Expected event log:
207 <version event>
208 <command event>
209 """
210 name = 'repo'
211 subcommands = ['init' 'this']
212 self._event_log_module.CommandEvent(name='repo', subcommands=subcommands)
213 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
214 log_path = self._event_log_module.Write(path=tempdir)
215 self._log_data = self.readLog(log_path)
216
217 self.assertEqual(len(self._log_data), 2)
218 command_event = self._log_data[1]
219 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
220 self.verifyCommonKeys(command_event, expected_event_name='command')
221 # Check for 'command' event specific fields.
222 self.assertIn('name', command_event)
223 self.assertIn('subcommands', command_event)
224 self.assertEqual(command_event['name'], name)
225 self.assertEqual(command_event['subcommands'], subcommands)
226
227 def test_def_params_event_repo_config(self):
228 """Test 'def_params' event data outputs only repo config keys.
229
230 Expected event log:
231 <version event>
232 <def_param event>
233 <def_param event>
234 """ 41 """
235 config = { 42 platform_utils.remove(socket_path, missing_ok=True)
236 'git.foo': 'bar', 43 data = b""
237 'repo.partialclone': 'true', 44 with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
238 'repo.partialclonefilter': 'blob:none', 45 sock.bind(socket_path)
239 } 46 sock.listen(0)
240 self._event_log_module.DefParamRepoEvents(config)
241
242 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
243 log_path = self._event_log_module.Write(path=tempdir)
244 self._log_data = self.readLog(log_path)
245
246 self.assertEqual(len(self._log_data), 3)
247 def_param_events = self._log_data[1:]
248 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
249
250 for event in def_param_events:
251 self.verifyCommonKeys(event, expected_event_name='def_param')
252 # Check for 'def_param' event specific fields.
253 self.assertIn('param', event)
254 self.assertIn('value', event)
255 self.assertTrue(event['param'].startswith('repo.'))
256
257 def test_def_params_event_no_repo_config(self):
258 """Test 'def_params' event data won't output non-repo config keys.
259
260 Expected event log:
261 <version event>
262 """
263 config = {
264 'git.foo': 'bar',
265 'git.core.foo2': 'baz',
266 }
267 self._event_log_module.DefParamRepoEvents(config)
268
269 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
270 log_path = self._event_log_module.Write(path=tempdir)
271 self._log_data = self.readLog(log_path)
272
273 self.assertEqual(len(self._log_data), 1)
274 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
275
276 def test_data_event_config(self):
277 """Test 'data' event data outputs all config keys.
278
279 Expected event log:
280 <version event>
281 <data event>
282 <data event>
283 """
284 config = {
285 'git.foo': 'bar',
286 'repo.partialclone': 'false',
287 'repo.syncstate.superproject.hassuperprojecttag': 'true',
288 'repo.syncstate.superproject.sys.argv': ['--', 'sync', 'protobuf'],
289 }
290 prefix_value = 'prefix'
291 self._event_log_module.LogDataConfigEvents(config, prefix_value)
292
293 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
294 log_path = self._event_log_module.Write(path=tempdir)
295 self._log_data = self.readLog(log_path)
296
297 self.assertEqual(len(self._log_data), 5)
298 data_events = self._log_data[1:]
299 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
300
301 for event in data_events:
302 self.verifyCommonKeys(event)
303 # Check for 'data' event specific fields.
304 self.assertIn('key', event)
305 self.assertIn('value', event)
306 key = event['key']
307 key = self.remove_prefix(key, f'{prefix_value}/')
308 value = event['value']
309 self.assertEqual(self._event_log_module.GetDataEventName(value), event['event'])
310 self.assertTrue(key in config and value == config[key])
311
312 def test_error_event(self):
313 """Test and validate 'error' event data is valid.
314
315 Expected event log:
316 <version event>
317 <error event>
318 """
319 msg = 'invalid option: --cahced'
320 fmt = 'invalid option: %s'
321 self._event_log_module.ErrorEvent(msg, fmt)
322 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
323 log_path = self._event_log_module.Write(path=tempdir)
324 self._log_data = self.readLog(log_path)
325
326 self.assertEqual(len(self._log_data), 2)
327 error_event = self._log_data[1]
328 self.verifyCommonKeys(self._log_data[0], expected_event_name='version')
329 self.verifyCommonKeys(error_event, expected_event_name='error')
330 # Check for 'error' event specific fields.
331 self.assertIn('msg', error_event)
332 self.assertIn('fmt', error_event)
333 self.assertEqual(error_event['msg'], msg)
334 self.assertEqual(error_event['fmt'], fmt)
335
336 def test_write_with_filename(self):
337 """Test Write() with a path to a file exits with None."""
338 self.assertIsNone(self._event_log_module.Write(path='path/to/file'))
339
340 def test_write_with_git_config(self):
341 """Test Write() uses the git config path when 'git config' call succeeds."""
342 with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir:
343 with mock.patch.object(self._event_log_module,
344 '_GetEventTargetPath', return_value=tempdir):
345 self.assertEqual(os.path.dirname(self._event_log_module.Write()), tempdir)
346
347 def test_write_no_git_config(self):
348 """Test Write() with no git config variable present exits with None."""
349 with mock.patch.object(self._event_log_module,
350 '_GetEventTargetPath', return_value=None):
351 self.assertIsNone(self._event_log_module.Write())
352
353 def test_write_non_string(self):
354 """Test Write() with non-string type for |path| throws TypeError."""
355 with self.assertRaises(TypeError):
356 self._event_log_module.Write(path=1234)
357
358 def test_write_socket(self):
359 """Test Write() with Unix domain socket for |path| and validate received traces."""
360 received_traces = []
361 with tempfile.TemporaryDirectory(prefix='test_server_sockets') as tempdir:
362 socket_path = os.path.join(tempdir, "server.sock")
363 server_ready = threading.Condition()
364 # Start "server" listening on Unix domain socket at socket_path.
365 try:
366 server_thread = threading.Thread(
367 target=serverLoggingThread,
368 args=(socket_path, server_ready, received_traces))
369 server_thread.start()
370
371 with server_ready: 47 with server_ready:
372 server_ready.wait(timeout=120) 48 server_ready.notify()
49 with sock.accept()[0] as conn:
50 while True:
51 recved = conn.recv(4096)
52 if not recved:
53 break
54 data += recved
55 received_traces.extend(data.decode("utf-8").splitlines())
56
373 57
58class EventLogTestCase(unittest.TestCase):
59 """TestCase for the EventLog module."""
60
61 PARENT_SID_KEY = "GIT_TRACE2_PARENT_SID"
62 PARENT_SID_VALUE = "parent_sid"
63 SELF_SID_REGEX = r"repo-\d+T\d+Z-.*"
64 FULL_SID_REGEX = r"^%s/%s" % (PARENT_SID_VALUE, SELF_SID_REGEX)
65
66 def setUp(self):
67 """Load the event_log module every time."""
68 self._event_log_module = None
69 # By default we initialize with the expected case where
70 # repo launches us (so GIT_TRACE2_PARENT_SID is set).
71 env = {
72 self.PARENT_SID_KEY: self.PARENT_SID_VALUE,
73 }
74 self._event_log_module = git_trace2_event_log.EventLog(env=env)
75 self._log_data = None
76
77 def verifyCommonKeys(
78 self, log_entry, expected_event_name=None, full_sid=True
79 ):
80 """Helper function to verify common event log keys."""
81 self.assertIn("event", log_entry)
82 self.assertIn("sid", log_entry)
83 self.assertIn("thread", log_entry)
84 self.assertIn("time", log_entry)
85
86 # Do basic data format validation.
87 if expected_event_name:
88 self.assertEqual(expected_event_name, log_entry["event"])
89 if full_sid:
90 self.assertRegex(log_entry["sid"], self.FULL_SID_REGEX)
91 else:
92 self.assertRegex(log_entry["sid"], self.SELF_SID_REGEX)
93 self.assertRegex(log_entry["time"], r"^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$")
94
95 def readLog(self, log_path):
96 """Helper function to read log data into a list."""
97 log_data = []
98 with open(log_path, mode="rb") as f:
99 for line in f:
100 log_data.append(json.loads(line))
101 return log_data
102
103 def remove_prefix(self, s, prefix):
104 """Return a copy string after removing |prefix| from |s|, if present or
105 the original string."""
106 if s.startswith(prefix):
107 return s[len(prefix) :]
108 else:
109 return s
110
111 def test_initial_state_with_parent_sid(self):
112 """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent."""
113 self.assertRegex(self._event_log_module.full_sid, self.FULL_SID_REGEX)
114
115 def test_initial_state_no_parent_sid(self):
116 """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set."""
117 # Setup an empty environment dict (no parent sid).
118 self._event_log_module = git_trace2_event_log.EventLog(env={})
119 self.assertRegex(self._event_log_module.full_sid, self.SELF_SID_REGEX)
120
121 def test_version_event(self):
122 """Test 'version' event data is valid.
123
124 Verify that the 'version' event is written even when no other
125 events are addded.
126
127 Expected event log:
128 <version event>
129 """
130 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
131 log_path = self._event_log_module.Write(path=tempdir)
132 self._log_data = self.readLog(log_path)
133
134 # A log with no added events should only have the version entry.
135 self.assertEqual(len(self._log_data), 1)
136 version_event = self._log_data[0]
137 self.verifyCommonKeys(version_event, expected_event_name="version")
138 # Check for 'version' event specific fields.
139 self.assertIn("evt", version_event)
140 self.assertIn("exe", version_event)
141 # Verify "evt" version field is a string.
142 self.assertIsInstance(version_event["evt"], str)
143
144 def test_start_event(self):
145 """Test and validate 'start' event data is valid.
146
147 Expected event log:
148 <version event>
149 <start event>
150 """
374 self._event_log_module.StartEvent() 151 self._event_log_module.StartEvent()
375 path = self._event_log_module.Write(path=f'af_unix:{socket_path}') 152 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
376 finally: 153 log_path = self._event_log_module.Write(path=tempdir)
377 server_thread.join(timeout=5) 154 self._log_data = self.readLog(log_path)
378 155
379 self.assertEqual(path, f'af_unix:stream:{socket_path}') 156 self.assertEqual(len(self._log_data), 2)
380 self.assertEqual(len(received_traces), 2) 157 start_event = self._log_data[1]
381 version_event = json.loads(received_traces[0]) 158 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
382 start_event = json.loads(received_traces[1]) 159 self.verifyCommonKeys(start_event, expected_event_name="start")
383 self.verifyCommonKeys(version_event, expected_event_name='version') 160 # Check for 'start' event specific fields.
384 self.verifyCommonKeys(start_event, expected_event_name='start') 161 self.assertIn("argv", start_event)
385 # Check for 'start' event specific fields. 162 self.assertTrue(isinstance(start_event["argv"], list))
386 self.assertIn('argv', start_event) 163
387 self.assertIsInstance(start_event['argv'], list) 164 def test_exit_event_result_none(self):
165 """Test 'exit' event data is valid when result is None.
166
167 We expect None result to be converted to 0 in the exit event data.
168
169 Expected event log:
170 <version event>
171 <exit event>
172 """
173 self._event_log_module.ExitEvent(None)
174 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
175 log_path = self._event_log_module.Write(path=tempdir)
176 self._log_data = self.readLog(log_path)
177
178 self.assertEqual(len(self._log_data), 2)
179 exit_event = self._log_data[1]
180 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
181 self.verifyCommonKeys(exit_event, expected_event_name="exit")
182 # Check for 'exit' event specific fields.
183 self.assertIn("code", exit_event)
184 # 'None' result should convert to 0 (successful) return code.
185 self.assertEqual(exit_event["code"], 0)
186
187 def test_exit_event_result_integer(self):
188 """Test 'exit' event data is valid when result is an integer.
189
190 Expected event log:
191 <version event>
192 <exit event>
193 """
194 self._event_log_module.ExitEvent(2)
195 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
196 log_path = self._event_log_module.Write(path=tempdir)
197 self._log_data = self.readLog(log_path)
198
199 self.assertEqual(len(self._log_data), 2)
200 exit_event = self._log_data[1]
201 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
202 self.verifyCommonKeys(exit_event, expected_event_name="exit")
203 # Check for 'exit' event specific fields.
204 self.assertIn("code", exit_event)
205 self.assertEqual(exit_event["code"], 2)
206
207 def test_command_event(self):
208 """Test and validate 'command' event data is valid.
209
210 Expected event log:
211 <version event>
212 <command event>
213 """
214 name = "repo"
215 subcommands = ["init" "this"]
216 self._event_log_module.CommandEvent(
217 name="repo", subcommands=subcommands
218 )
219 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
220 log_path = self._event_log_module.Write(path=tempdir)
221 self._log_data = self.readLog(log_path)
222
223 self.assertEqual(len(self._log_data), 2)
224 command_event = self._log_data[1]
225 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
226 self.verifyCommonKeys(command_event, expected_event_name="command")
227 # Check for 'command' event specific fields.
228 self.assertIn("name", command_event)
229 self.assertIn("subcommands", command_event)
230 self.assertEqual(command_event["name"], name)
231 self.assertEqual(command_event["subcommands"], subcommands)
232
233 def test_def_params_event_repo_config(self):
234 """Test 'def_params' event data outputs only repo config keys.
235
236 Expected event log:
237 <version event>
238 <def_param event>
239 <def_param event>
240 """
241 config = {
242 "git.foo": "bar",
243 "repo.partialclone": "true",
244 "repo.partialclonefilter": "blob:none",
245 }
246 self._event_log_module.DefParamRepoEvents(config)
247
248 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
249 log_path = self._event_log_module.Write(path=tempdir)
250 self._log_data = self.readLog(log_path)
251
252 self.assertEqual(len(self._log_data), 3)
253 def_param_events = self._log_data[1:]
254 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
255
256 for event in def_param_events:
257 self.verifyCommonKeys(event, expected_event_name="def_param")
258 # Check for 'def_param' event specific fields.
259 self.assertIn("param", event)
260 self.assertIn("value", event)
261 self.assertTrue(event["param"].startswith("repo."))
262
263 def test_def_params_event_no_repo_config(self):
264 """Test 'def_params' event data won't output non-repo config keys.
265
266 Expected event log:
267 <version event>
268 """
269 config = {
270 "git.foo": "bar",
271 "git.core.foo2": "baz",
272 }
273 self._event_log_module.DefParamRepoEvents(config)
274
275 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
276 log_path = self._event_log_module.Write(path=tempdir)
277 self._log_data = self.readLog(log_path)
278
279 self.assertEqual(len(self._log_data), 1)
280 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
281
282 def test_data_event_config(self):
283 """Test 'data' event data outputs all config keys.
284
285 Expected event log:
286 <version event>
287 <data event>
288 <data event>
289 """
290 config = {
291 "git.foo": "bar",
292 "repo.partialclone": "false",
293 "repo.syncstate.superproject.hassuperprojecttag": "true",
294 "repo.syncstate.superproject.sys.argv": ["--", "sync", "protobuf"],
295 }
296 prefix_value = "prefix"
297 self._event_log_module.LogDataConfigEvents(config, prefix_value)
298
299 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
300 log_path = self._event_log_module.Write(path=tempdir)
301 self._log_data = self.readLog(log_path)
302
303 self.assertEqual(len(self._log_data), 5)
304 data_events = self._log_data[1:]
305 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
306
307 for event in data_events:
308 self.verifyCommonKeys(event)
309 # Check for 'data' event specific fields.
310 self.assertIn("key", event)
311 self.assertIn("value", event)
312 key = event["key"]
313 key = self.remove_prefix(key, f"{prefix_value}/")
314 value = event["value"]
315 self.assertEqual(
316 self._event_log_module.GetDataEventName(value), event["event"]
317 )
318 self.assertTrue(key in config and value == config[key])
319
320 def test_error_event(self):
321 """Test and validate 'error' event data is valid.
322
323 Expected event log:
324 <version event>
325 <error event>
326 """
327 msg = "invalid option: --cahced"
328 fmt = "invalid option: %s"
329 self._event_log_module.ErrorEvent(msg, fmt)
330 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
331 log_path = self._event_log_module.Write(path=tempdir)
332 self._log_data = self.readLog(log_path)
333
334 self.assertEqual(len(self._log_data), 2)
335 error_event = self._log_data[1]
336 self.verifyCommonKeys(self._log_data[0], expected_event_name="version")
337 self.verifyCommonKeys(error_event, expected_event_name="error")
338 # Check for 'error' event specific fields.
339 self.assertIn("msg", error_event)
340 self.assertIn("fmt", error_event)
341 self.assertEqual(error_event["msg"], msg)
342 self.assertEqual(error_event["fmt"], fmt)
343
344 def test_write_with_filename(self):
345 """Test Write() with a path to a file exits with None."""
346 self.assertIsNone(self._event_log_module.Write(path="path/to/file"))
347
348 def test_write_with_git_config(self):
349 """Test Write() uses the git config path when 'git config' call
350 succeeds."""
351 with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir:
352 with mock.patch.object(
353 self._event_log_module,
354 "_GetEventTargetPath",
355 return_value=tempdir,
356 ):
357 self.assertEqual(
358 os.path.dirname(self._event_log_module.Write()), tempdir
359 )
360
361 def test_write_no_git_config(self):
362 """Test Write() with no git config variable present exits with None."""
363 with mock.patch.object(
364 self._event_log_module, "_GetEventTargetPath", return_value=None
365 ):
366 self.assertIsNone(self._event_log_module.Write())
367
368 def test_write_non_string(self):
369 """Test Write() with non-string type for |path| throws TypeError."""
370 with self.assertRaises(TypeError):
371 self._event_log_module.Write(path=1234)
372
373 def test_write_socket(self):
374 """Test Write() with Unix domain socket for |path| and validate received
375 traces."""
376 received_traces = []
377 with tempfile.TemporaryDirectory(
378 prefix="test_server_sockets"
379 ) as tempdir:
380 socket_path = os.path.join(tempdir, "server.sock")
381 server_ready = threading.Condition()
382 # Start "server" listening on Unix domain socket at socket_path.
383 try:
384 server_thread = threading.Thread(
385 target=serverLoggingThread,
386 args=(socket_path, server_ready, received_traces),
387 )
388 server_thread.start()
389
390 with server_ready:
391 server_ready.wait(timeout=120)
392
393 self._event_log_module.StartEvent()
394 path = self._event_log_module.Write(
395 path=f"af_unix:{socket_path}"
396 )
397 finally:
398 server_thread.join(timeout=5)
399
400 self.assertEqual(path, f"af_unix:stream:{socket_path}")
401 self.assertEqual(len(received_traces), 2)
402 version_event = json.loads(received_traces[0])
403 start_event = json.loads(received_traces[1])
404 self.verifyCommonKeys(version_event, expected_event_name="version")
405 self.verifyCommonKeys(start_event, expected_event_name="start")
406 # Check for 'start' event specific fields.
407 self.assertIn("argv", start_event)
408 self.assertIsInstance(start_event["argv"], list)