Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
199 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# pylint: disable=unused-argument, no-self-use | ||
from __future__ import absolute_import | ||
|
||
from six.moves import http_client | ||
from swaggapi.api.builder.server.response import Response | ||
from swaggapi.api.builder.server.exceptions import BadRequest | ||
from swaggapi.api.builder.server.request import DjangoRequestView | ||
|
||
from rotest.api.common.models import StatisticsRequestModel | ||
from rotest.api.test_control.middleware import session_middleware | ||
from rotest.core.utils.test_statistics import clean_data, collect_durations | ||
from rotest.api.common.responses import (TestStatisticsResponse, | ||
FailureResponseModel) | ||
|
||
|
||
class GetTestStatistics(DjangoRequestView): | ||
"""Get statistics for a test or component.""" | ||
URI = "tests/get_statistics" | ||
DEFAULT_MODEL = StatisticsRequestModel | ||
DEFAULT_RESPONSES = { | ||
http_client.OK: TestStatisticsResponse, | ||
http_client.BAD_REQUEST: FailureResponseModel | ||
} | ||
TAGS = { | ||
"get": [] | ||
} | ||
|
||
@session_middleware | ||
def get(self, request, sessions, *args, **kwargs): | ||
"""Initialize the tests run data.""" | ||
parameters = {"test_name": request.model.test_name} | ||
if request.model.max_sample_size is not None: | ||
parameters["max_size"] = request.model.max_sample_size | ||
|
||
test_durations = collect_durations(**parameters) | ||
if len(test_durations) < 1: | ||
raise BadRequest("No test history found!") | ||
|
||
parameters = {"durations": test_durations} | ||
if request.model.min_duration_cut is not None: | ||
parameters["min_duration_cut"] = request.model.min_duration_cut | ||
if request.model.max_iterations is not None: | ||
parameters["max_iterations"] = request.model.max_iterations | ||
if request.model.acceptable_ratio is not None: | ||
parameters["acceptable_ratio"] = request.model.acceptable_ratio | ||
|
||
test_durations = clean_data(**parameters) | ||
if len(test_durations) < 1: | ||
raise BadRequest("Test history disparity too wide!") | ||
|
||
response = { | ||
"min": min(test_durations), | ||
"avg": sum(test_durations) / len(test_durations), | ||
"max": max(test_durations) | ||
} | ||
|
||
return Response(response, status=http_client.OK) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"""Module for statistics collection on tests.""" | ||
from __future__ import absolute_import | ||
from statistics import mean, median, pstdev | ||
|
||
from rotest.core.models import CaseData | ||
|
||
|
||
CUT_OFF_FACTOR = 1.5 | ||
|
||
|
||
def collect_durations(test_name, max_size=300): | ||
"""Return re durations of successful runs tests with the given name. | ||
Args: | ||
test_name (str): name of the test to search, e.g. "MyTest.test_method". | ||
max_size (number): maximal number of tests to collect. | ||
Returns: | ||
list. collected tests after filtering. | ||
""" | ||
latest_tests = CaseData.objects.filter(name=test_name, | ||
exception_type=0, | ||
start_time__isnull=False, | ||
end_time__isnull=False) | ||
|
||
latest_tests = latest_tests.order_by('-id') | ||
durations = ((test.end_time - test.start_time).total_seconds() | ||
for test in latest_tests) | ||
|
||
durations = [x for x in durations if x > 0] | ||
|
||
return durations[:max_size] | ||
|
||
|
||
def remove_anomalies(durations): | ||
"""Return a list with less anomalies in the numeric values. | ||
Args: | ||
durations (list): list of numeric values. | ||
Returns: | ||
list. list with less anomalies. | ||
""" | ||
avg = (mean(durations) + median(durations)) / 2 | ||
deviation = pstdev(durations) | ||
cut_off = deviation * CUT_OFF_FACTOR | ||
lower_limit = avg - cut_off | ||
upper_limit = avg + cut_off | ||
return [x for x in durations if lower_limit < x < upper_limit] | ||
|
||
|
||
def clean_data(durations, min_duration_cut=0.5, | ||
max_iterations=3, acceptable_ratio=2): | ||
"""Return a list with less anomalies in the numeric values. | ||
Args: | ||
durations (list): list of numeric values. | ||
min_duration_cut (number): ignore tests under the given duration. | ||
max_iterations (number): max anomalies removal iterations. | ||
acceptable_ratio (number): acceptable ration between max and min | ||
values, under which don't try to remove anomalies. | ||
Returns: | ||
list. filtered list of durations. | ||
""" | ||
durations = [x for x in durations if x > min_duration_cut] | ||
iteration_index = 0 | ||
while len(durations) > 1 and iteration_index < max_iterations: | ||
if min(durations) * acceptable_ratio >= max(durations): | ||
break | ||
|
||
durations = remove_anomalies(durations) | ||
iteration_index += 1 | ||
|
||
return durations |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters