2828from pymysql .err import IntegrityError
2929from sqlalchemy import and_ , func , select
3030from sqlalchemy .sql import label
31- from sqlalchemy .sql .functions import count
3231from werkzeug .utils import secure_filename
3332
3433from database import DeclEnum , create_session
@@ -249,8 +248,8 @@ class Workflow_builds(DeclEnum):
249248class Artifact_names (DeclEnum ):
250249 """Define CCExtractor GitHub Artifacts names."""
251250
252- linux = "CCExtractor Linux build"
253- windows = "CCExtractor Windows x64 Release build"
251+ linux = "CCExtractor Linux build (with migrations) "
252+ windows = "CCExtractor Windows x64 Release build (with migrations) "
254253
255254
256255def is_valid_commit_hash (commit : Optional [str ]) -> bool :
@@ -2276,14 +2275,16 @@ def start_ci():
22762275 return json .dumps ({'msg' : 'EOL' })
22772276
22782277
2279- def update_build_badge (status , test ) -> None :
2278+ def update_build_badge (status , test , test_results = None ) -> None :
22802279 """
22812280 Build status badge for current test to be displayed on sample-platform.
22822281
22832282 :param status: current testing status
22842283 :type status: str
22852284 :param test: current commit that is tested
22862285 :type test: Test
2286+ :param test_results: pre-computed results from get_test_results; if None, fetched internally
2287+ :type test_results: list | None
22872288 :return: null
22882289 :rtype: null
22892290 """
@@ -2294,7 +2295,8 @@ def update_build_badge(status, test) -> None:
22942295 shutil .copyfile (original_location , build_status_location )
22952296 g .log .info ('Build badge updated successfully!' )
22962297
2297- test_results = get_test_results (test )
2298+ if test_results is None :
2299+ test_results = get_test_results (test )
22982300 test_ids_to_update = []
22992301 for category_results in test_results :
23002302 test_ids_to_update .extend ([test ['test' ].id for test in category_results ['tests' ] if not test ['error' ]])
@@ -2429,33 +2431,15 @@ def progress_type_request(log, test, test_id, request) -> bool:
24292431 message = 'Tests aborted due to an error; please check'
24302432
24312433 elif status == TestStatus .completed :
2432- # Determine if success or failure
2433- # It fails if any of these happen:
2434- # - A crash (unexpected exit code)
2435- # - A not None value on the "got" of a TestResultFile (
2436- # meaning the hashes do not match)
2437- crashes = g .db .query (count (TestResult .exit_code )).filter (
2438- and_ (
2439- TestResult .test_id == test .id ,
2440- TestResult .exit_code != TestResult .expected_rc
2441- )).scalar ()
2442- results_zero_rc = select (RegressionTest .id ).filter (
2443- RegressionTest .expected_rc == 0
2434+ test_results = get_test_results (test )
2435+ has_failures = any (
2436+ t ['error' ]
2437+ for category in test_results
2438+ for t in category ['tests' ]
24442439 )
2445- results = g .db .query (count (TestResultFile .got )).filter (
2446- and_ (
2447- TestResultFile .test_id == test .id ,
2448- TestResultFile .regression_test_id .in_ (
2449- results_zero_rc .select ()
2450- ),
2451- TestResultFile .got .isnot (None )
2452- )
2453- ).scalar ()
2454- log .debug (f'[Test: { test .id } ] Test completed: { crashes } crashes, { results } results' )
2455- if crashes > 0 or results > 0 :
2440+ if has_failures :
24562441 state = Status .FAILURE
24572442 message = 'Not all tests completed successfully, please check'
2458-
24592443 else :
24602444 state = Status .SUCCESS
24612445 message = 'Tests completed'
@@ -2468,7 +2452,7 @@ def progress_type_request(log, test, test_id, request) -> bool:
24682452 message = 'All tests passed'
24692453 else :
24702454 message = 'Not all tests completed successfully, please check'
2471- update_build_badge (state , test )
2455+ update_build_badge (state , test , test_results )
24722456
24732457 else :
24742458 message = progress .message
@@ -2526,29 +2510,17 @@ def update_final_status():
25262510 TestProgress .test_id .in_ (platform_tests .select ())
25272511 )
25282512 )
2529- in_progress_statuses = [TestStatus .preparation , TestStatus .completed , TestStatus .canceled ]
2530- finished_tests_progress = g .db .query (TestProgress ).filter (
2531- and_ (
2532- TestProgress .test_id .in_ (finished_tests .select ()),
2533- TestProgress .status .in_ (in_progress_statuses )
2534- )
2535- ).subquery ()
25362513 times = g .db .query (
2537- finished_tests_progress .c .test_id ,
2538- label ('time' , func .group_concat (finished_tests_progress .c .timestamp ))
2539- ).group_by (finished_tests_progress .c .test_id ).all ()
2514+ TestProgress .test_id ,
2515+ label ('start_time' , func .min (TestProgress .timestamp )),
2516+ label ('end_time' , func .max (TestProgress .timestamp ))
2517+ ).filter (
2518+ TestProgress .test_id .in_ (finished_tests )
2519+ ).group_by (TestProgress .test_id ).all ()
25402520
25412521 for p in times :
2542- parts = p .time .split (',' )
2543- try :
2544- # Try parsing with microsecond precision first
2545- start = datetime .datetime .strptime (parts [0 ], '%Y-%m-%d %H:%M:%S.%f' )
2546- end = datetime .datetime .strptime (parts [- 1 ], '%Y-%m-%d %H:%M:%S.%f' )
2547- except ValueError :
2548- # Fall back to format without microseconds
2549- start = datetime .datetime .strptime (parts [0 ], '%Y-%m-%d %H:%M:%S' )
2550- end = datetime .datetime .strptime (parts [- 1 ], '%Y-%m-%d %H:%M:%S' )
2551- total_time += int ((end - start ).total_seconds ())
2522+ duration = max (0 , int ((p .end_time - p .start_time ).total_seconds ()))
2523+ total_time += duration
25522524
25532525 if len (times ) != 0 :
25542526 average_time = total_time // len (times )
@@ -2559,10 +2531,15 @@ def update_final_status():
25592531 safe_db_commit (g .db , "setting new average time" )
25602532
25612533 else :
2562- all_results = TestResult .query .count ()
2563- regression_test_count = RegressionTest .query .count ()
2564- number_test = all_results / regression_test_count
2565- updated_average = float (current_average .value ) * (number_test - 1 )
2534+ # Count actual finished tests for this platform
2535+ platform_tests = g .db .query (Test .id ).filter (Test .platform == test .platform ).subquery ()
2536+ number_test = g .db .query (func .count (func .distinct (TestProgress .test_id ))).filter (
2537+ and_ (
2538+ TestProgress .status .in_ ([TestStatus .completed , TestStatus .canceled ]),
2539+ TestProgress .test_id .in_ (platform_tests )
2540+ )
2541+ ).scalar () or 0
2542+
25662543 pr = test .progress_data ()
25672544 end_time = pr ['end' ]
25682545 start_time = pr ['start' ]
@@ -2573,9 +2550,14 @@ def update_final_status():
25732550 if start_time .tzinfo is not None :
25742551 start_time = start_time .replace (tzinfo = None )
25752552
2576- last_running_test = end_time - start_time
2577- updated_average = updated_average + last_running_test .total_seconds ()
2578- current_average .value = 0 if number_test == 0 else updated_average // number_test
2553+ last_duration = max (0 , int ((end_time - start_time ).total_seconds ()))
2554+
2555+ if number_test > 1 :
2556+ updated_average = (float (current_average .value ) * (number_test - 1 ) + last_duration ) / number_test
2557+ else :
2558+ updated_average = last_duration
2559+
2560+ current_average .value = max (0 , int (updated_average ))
25792561 safe_db_commit (g .db , "updating average time" )
25802562 log .info (f'average time updated to { str (current_average .value )} ' )
25812563
0 commit comments