diff --git a/mod_test/controllers.py b/mod_test/controllers.py index bb34319b..fb35dab5 100644 --- a/mod_test/controllers.py +++ b/mod_test/controllers.py @@ -1,19 +1,16 @@ """Logic to find all tests, their progress and details of individual test.""" import os -from datetime import datetime from typing import Any, Dict, List from flask import (Blueprint, Response, abort, g, jsonify, redirect, request, url_for) -from sqlalchemy import and_, func -from sqlalchemy.sql import label +from sqlalchemy import and_ from decorators import template_renderer from exceptions import TestNotFoundException from mod_auth.controllers import check_access_rights, login_required from mod_auth.models import Role -from mod_ci.models import GcpInstance from mod_customized.models import TestFork from mod_home.models import CCExtractorVersion, GeneralData from mod_regression.models import (Category, RegressionTestOutput, @@ -133,47 +130,23 @@ def get_data_for_test(test, title=None) -> Dict[str, Any]: if title is None: title = f"test {test.id}" - hours = 0.00 - minutes = 0.00 - queued_tests = 0 - - """ - evaluating estimated time if the test is still in queue - estimated time = (number of tests already in queue + 1) * (average time of that platform) - - (time already spend by those tests) - calculates time in minutes and hours - """ + # Calculate average runtime for this platform (used when test hasn't started yet) + avg_minutes = 0 if len(test.progress) == 0: - var_average = 'average_time_' + test.platform.value - - # get average build and prep time. - prep_average_key = 'avg_prep_time_' + test.platform.value - average_prep_time = int(float(GeneralData.query.filter(GeneralData.key == prep_average_key).first().value)) - - test_progress_last_entry = g.db.query(func.max(TestProgress.test_id)).first() - last_test_id = test_progress_last_entry[0] if test_progress_last_entry is not None else 0 - queued_gcp_instance = g.db.query(GcpInstance.test_id).filter(GcpInstance.test_id < test.id).subquery() - queued_gcp_instance_entries = g.db.query(Test.id).filter( - and_(Test.id.in_(queued_gcp_instance), Test.platform == test.platform) - ).subquery() - gcp_instance_test = g.db.query(TestProgress.test_id, label('time', func.group_concat( - TestProgress.timestamp))).filter(TestProgress.test_id.in_(queued_gcp_instance_entries)).group_by( - TestProgress.test_id).all() - number_gcp_instance_test = g.db.query(Test.id).filter( - and_(Test.id > last_test_id, Test.id < test.id, Test.platform == test.platform) - ).count() - average_duration = float(GeneralData.query.filter(GeneralData.key == var_average).first().value) - queued_tests = number_gcp_instance_test - time_run = 0.00 - for pr_test in gcp_instance_test: - timestamps = pr_test.time.split(',') - start = datetime.strptime(timestamps[0], '%Y-%m-%d %H:%M:%S') - end = datetime.strptime(timestamps[-1], '%Y-%m-%d %H:%M:%S') - time_run += (end - start).total_seconds() - # subtracting current running tests - total = average_prep_time + average_duration - time_run - minutes = (total % 3600) // 60 - hours = total // 3600 + try: + avg_time_key = 'average_time_' + test.platform.value + prep_time_key = 'avg_prep_time_' + test.platform.value + + avg_time_record = GeneralData.query.filter(GeneralData.key == avg_time_key).first() + prep_time_record = GeneralData.query.filter(GeneralData.key == prep_time_key).first() + + avg_duration = float(avg_time_record.value) if avg_time_record else 0 + avg_prep = float(prep_time_record.value) if prep_time_record else 0 + + # Total average time in minutes + avg_minutes = int((avg_duration + avg_prep) / 60) + except (ValueError, AttributeError): + avg_minutes = 0 results = get_test_results(test) @@ -182,9 +155,7 @@ def get_data_for_test(test, title=None) -> Dict[str, Any]: 'TestType': TestType, 'results': results, 'title': title, - 'next': queued_tests, - 'min': minutes, - 'hr': hours + 'avg_minutes': avg_minutes } diff --git a/templates/test/by_id.html b/templates/test/by_id.html index 6818b47d..d9a6c5cd 100644 --- a/templates/test/by_id.html +++ b/templates/test/by_id.html @@ -101,7 +101,7 @@

Test progress for {{ title }}

{% else %} -

This test is still queued. There are {{ next }} tests queued before this one. Based on the average runtime, this test will be finished in {{ hr }} hours {{ min }} minutes.

+

Your test VM is being provisioned.{% if avg_minutes > 0 %} Tests on {{ test.platform.description }} typically complete in about {{ avg_minutes }} minutes.{% endif %}

{% endif %} {% if test.finished %} {% if test.failed %} diff --git a/tests/test_test/test_controllers.py b/tests/test_test/test_controllers.py index e2f28259..7d940d14 100644 --- a/tests/test_test/test_controllers.py +++ b/tests/test_test/test_controllers.py @@ -6,7 +6,7 @@ from mod_regression.models import RegressionTest from mod_test.models import (Test, TestPlatform, TestProgress, TestResult, TestResultFile, TestStatus) -from tests.base import BaseTestCase, create_mock_db_query +from tests.base import BaseTestCase from tests.test_auth.test_controllers import MockUser @@ -122,25 +122,30 @@ def test_ccextractor_version_not_found(self): self.assertEqual(response.status_code, 404) self.assert_template_used('test/test_not_found.html') - @mock.patch('mod_test.controllers.g') @mock.patch('mod_test.controllers.GeneralData') @mock.patch('mod_test.controllers.Category') - @mock.patch('mod_test.controllers.TestProgress') - def test_data_for_test(self, mock_test_progress, mock_category, mock_gen_data, mock_g): + def test_data_for_test(self, mock_category, mock_gen_data): """Test get_data_for_test method.""" from mod_test.controllers import get_data_for_test mock_test = mock.MagicMock() + mock_test.progress = [] # No progress yet, so avg_minutes should be calculated + mock_test.platform.value = 'linux' - # Set up mock db query chain to avoid AsyncMock behavior in Python 3.13+ - create_mock_db_query(mock_g) + # Mock GeneralData query responses for average times + mock_avg_time = mock.MagicMock() + mock_avg_time.value = '300' # 5 minutes in seconds + mock_prep_time = mock.MagicMock() + mock_prep_time.value = '60' # 1 minute in seconds + mock_gen_data.query.filter.return_value.first.side_effect = [mock_avg_time, mock_prep_time] result = get_data_for_test(mock_test) self.assertIsInstance(result, dict) - self.assertEqual(6, mock_g.db.query.call_count) + self.assertIn('avg_minutes', result) + self.assertEqual(result['avg_minutes'], 6) # (300 + 60) / 60 = 6 minutes mock_category.query.filter.assert_called_once() - mock_gen_data.query.filter.assert_called() + self.assertEqual(mock_gen_data.query.filter.call_count, 2) @mock.patch('mod_test.controllers.Test') def test_get_json_data_no_test(self, mock_test):