#!/usr/bin/env python3
import time
import threading
import unittest
from collections import namedtuple
from pathlib import Path
from collections . abc import Sequence
import openpilot . system . loggerd . deleter as deleter
from openpilot . common . timeout import Timeout , TimeoutException
from openpilot . system . loggerd . tests . loggerd_tests_common import UploaderTestCase
Stats = namedtuple ( " Stats " , [ ' f_bavail ' , ' f_blocks ' , ' f_frsize ' ] )
class TestDeleter ( UploaderTestCase ) :
def fake_statvfs ( self , d ) :
return self . fake_stats
def setUp ( self ) :
self . f_type = " fcamera.hevc "
super ( ) . setUp ( )
self . fake_stats = Stats ( f_bavail = 0 , f_blocks = 10 , f_frsize = 4096 )
deleter . os . statvfs = self . fake_statvfs
def start_thread ( self ) :
self . end_event = threading . Event ( )
self . del_thread = threading . Thread ( target = deleter . deleter_thread , args = [ self . end_event ] )
self . del_thread . daemon = True
self . del_thread . start ( )
def join_thread ( self ) :
self . end_event . set ( )
self . del_thread . join ( )
def test_delete ( self ) :
f_path = self . make_file_with_data ( self . seg_dir , self . f_type , 1 )
self . start_thread ( )
try :
with Timeout ( 2 , " Timeout waiting for file to be deleted " ) :
while f_path . exists ( ) :
time . sleep ( 0.01 )
finally :
self . join_thread ( )
def assertDeleteOrder ( self , f_paths : Sequence [ Path ] , timeout : int = 5 ) - > None :
deleted_order = [ ]
self . start_thread ( )
try :
with Timeout ( timeout , " Timeout waiting for files to be deleted " ) :
while True :
for f in f_paths :
if not f . exists ( ) and f not in deleted_order :
deleted_order . append ( f )
if len ( deleted_order ) == len ( f_paths ) :
break
time . sleep ( 0.01 )
except TimeoutException :
print ( " Not deleted: " , [ f for f in f_paths if f not in deleted_order ] )
raise
finally :
self . join_thread ( )
self . assertEqual ( deleted_order , f_paths , " Files not deleted in expected order " )
def test_delete_order ( self ) :
self . assertDeleteOrder ( [
self . make_file_with_data ( self . seg_format . format ( 0 ) , self . f_type ) ,
self . make_file_with_data ( self . seg_format . format ( 1 ) , self . f_type ) ,
self . make_file_with_data ( self . seg_format2 . format ( 0 ) , self . f_type ) ,
] )
def test_delete_many_preserved ( self ) :
self . assertDeleteOrder ( [
self . make_file_with_data ( self . seg_format . format ( 0 ) , self . f_type ) ,
self . make_file_with_data ( self . seg_format . format ( 1 ) , self . f_type , preserve_xattr = deleter . PRESERVE_ATTR_VALUE ) ,
self . make_file_with_data ( self . seg_format . format ( 2 ) , self . f_type ) ,
] + [
self . make_file_with_data ( self . seg_format2 . format ( i ) , self . f_type , preserve_xattr = deleter . PRESERVE_ATTR_VALUE )
for i in range ( 5 )
] )
def test_delete_last ( self ) :
self . assertDeleteOrder ( [
self . make_file_with_data ( self . seg_format . format ( 1 ) , self . f_type ) ,
self . make_file_with_data ( self . seg_format2 . format ( 0 ) , self . f_type ) ,
self . make_file_with_data ( self . seg_format . format ( 0 ) , self . f_type , preserve_xattr = deleter . PRESERVE_ATTR_VALUE ) ,
self . make_file_with_data ( " boot " , self . seg_format [ : - 4 ] ) ,
self . make_file_with_data ( " crash " , self . seg_format2 [ : - 4 ] ) ,
] )
def test_no_delete_when_available_space ( self ) :
f_path = self . make_file_with_data ( self . seg_dir , self . f_type )
block_size = 4096
available = ( 10 * 1024 * 1024 * 1024 ) / block_size # 10GB free
self . fake_stats = Stats ( f_bavail = available , f_blocks = 10 , f_frsize = block_size )
self . start_thread ( )
start_time = time . monotonic ( )
while f_path . exists ( ) and time . monotonic ( ) - start_time < 2 :
time . sleep ( 0.01 )
self . join_thread ( )
self . assertTrue ( f_path . exists ( ) , " File deleted with available space " )
def test_no_delete_with_lock_file ( self ) :
f_path = self . make_file_with_data ( self . seg_dir , self . f_type , lock = True )
self . start_thread ( )
start_time = time . monotonic ( )
while f_path . exists ( ) and time . monotonic ( ) - start_time < 2 :
time . sleep ( 0.01 )
self . join_thread ( )
self . assertTrue ( f_path . exists ( ) , " File deleted when locked " )
if __name__ == " __main__ " :
unittest . main ( )