@@ -345,5 +345,68 @@ def test_only_first_part_has_header(self):
345345 self .assertEqual (second [0 ], "rowB,2" )
346346
347347
348+ class NetworkStreamCompressionTests (unittest .TestCase ):
349+ """Network streams (nw_conn/nw_epoll/nw_sockopt/nw_drop) must be compressed
350+ and uploaded exactly like every other trace stream — both at shutdown
351+ (force_flush) and on mid-trace rotation."""
352+
353+ def setUp (self ):
354+ self .tmp = tempfile .mkdtemp ()
355+ self .output_dir = os .path .join (self .tmp , "trace" )
356+ self .upload = FakeUploadManager ()
357+ self .wm = SilentWriteManager (
358+ output_dir = self .output_dir ,
359+ upload_manager = self .upload ,
360+ automatic_upload = True ,
361+ )
362+
363+ def tearDown (self ):
364+ import shutil
365+ try :
366+ self .wm .close_handles ()
367+ except Exception :
368+ pass
369+ shutil .rmtree (self .tmp , ignore_errors = True )
370+
371+ def test_all_network_streams_registered_for_rotation (self ):
372+ # Every network stream must be in the generic rotation registry so a
373+ # slow stream is compressed+uploaded mid-trace, not just at shutdown.
374+ for key in ("nw_conn" , "nw_epoll" , "nw_sockopt" , "nw_drop" ):
375+ self .assertIn (key , self .wm ._streams , key )
376+
377+ @unittest .skipUnless (HAS_ZSTD , "zstandard not installed" )
378+ def test_force_flush_compresses_network_streams (self ):
379+ self .wm .append_conn_log ("ts,CONNECT,1,1,proc,AF_INET" )
380+ self .wm .append_epoll_log ("ts,EPOLL_WAIT,1,1,proc" )
381+ self .wm .append_sockopt_log ("ts,SET,1,proc,3" )
382+ self .wm .append_drop_log ("ts,PACKET_DROP,1,proc,TCP" )
383+
384+ self .wm .force_flush ()
385+
386+ # Each stream produced exactly one compressed upload under its own subdir.
387+ self .assertEqual (len (self .upload .uploaded ), 4 )
388+ subdirs = {os .path .basename (os .path .dirname (p )) for p in self .upload .uploaded }
389+ for sub in ("nw_conn" , "nw_epoll" , "nw_sockopt" , "nw_drop" ):
390+ self .assertIn (sub , subdirs , sub )
391+ for p in self .upload .uploaded :
392+ self .assertTrue (p .endswith (".csv.zst" ), p )
393+ self .assertTrue (os .path .exists (p ))
394+
395+ @unittest .skipUnless (HAS_ZSTD , "zstandard not installed" )
396+ def test_network_threshold_rotation_compresses (self ):
397+ # Dropping the threshold forces a mid-trace rotation, which must
398+ # compress+upload the rotated file just like the continuous streams.
399+ self .wm .nw_conn_max_events = 3
400+ for i in range (7 ):
401+ self .wm .append_conn_log (f"row{ i } " )
402+
403+ # 7 events at a threshold of 3 → 2 full rotations (2 uploads), with 1
404+ # event left buffered (force_flush is not called here).
405+ self .assertEqual (len (self .upload .uploaded ), 2 )
406+ for p in self .upload .uploaded :
407+ self .assertTrue (p .endswith (".csv.zst" ), p )
408+ self .assertEqual (os .path .basename (os .path .dirname (p )), "nw_conn" )
409+
410+
348411if __name__ == "__main__" :
349412 unittest .main ()
0 commit comments