Bug 1831945 - Add a toggle for replicates to the CompareView. (#7703)

This commit is contained in:
Gregory Mierzwinski 2023-06-07 10:35:45 -04:00 коммит произвёл GitHub
Родитель c78cd4e78d
Коммит 4d7f863362
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 96 добавлений и 9 удалений

Просмотреть файл

@ -162,6 +162,8 @@ const compareTableControlsNode = (
showOnlyComparable: '0', showOnlyComparable: '0',
showOnlyConfident: '0', showOnlyConfident: '0',
showOnlyNoise: '0', showOnlyNoise: '0',
originalProject: 'try',
newProject: 'mozilla-central',
filter: null, filter: null,
}} }}
location={{ location={{
@ -246,7 +248,13 @@ test('toggle all buttons should update the URL params', async () => {
fireEvent.click(showImportant); fireEvent.click(showImportant);
expect(mockUpdateParams).toHaveBeenLastCalledWith( expect(mockUpdateParams).toHaveBeenLastCalledWith(
{ page: 1, showOnlyImportant: 1 }, { page: 1, showOnlyImportant: 1 },
['filter', 'showOnlyComparable', 'showOnlyConfident', 'showOnlyNoise'], [
'filter',
'showOnlyComparable',
'showOnlyConfident',
'showOnlyNoise',
'replicates',
],
); );
const hideUncertain = await waitFor(() => const hideUncertain = await waitFor(() =>
@ -259,7 +267,7 @@ test('toggle all buttons should update the URL params', async () => {
showOnlyImportant: 1, showOnlyImportant: 1,
showOnlyConfident: 1, showOnlyConfident: 1,
}, },
['filter', 'showOnlyComparable', 'showOnlyNoise'], ['filter', 'showOnlyComparable', 'showOnlyNoise', 'replicates'],
); );
const showNoise = await waitFor(() => getByText(filterText.showNoise)); const showNoise = await waitFor(() => getByText(filterText.showNoise));
@ -271,7 +279,7 @@ test('toggle all buttons should update the URL params', async () => {
showOnlyConfident: 1, showOnlyConfident: 1,
showOnlyNoise: 1, showOnlyNoise: 1,
}, },
['filter', 'showOnlyComparable'], ['filter', 'showOnlyComparable', 'replicates'],
); );
const hideUncomparable = await waitFor(() => const hideUncomparable = await waitFor(() =>
@ -286,6 +294,22 @@ test('toggle all buttons should update the URL params', async () => {
showOnlyNoise: 1, showOnlyNoise: 1,
showOnlyComparable: 1, showOnlyComparable: 1,
}, },
['filter', 'replicates'],
);
const useReplicates = await waitFor(() =>
getByText('Use replicates (try-only)'),
);
fireEvent.click(useReplicates);
expect(mockUpdateParams).toHaveBeenLastCalledWith(
{
page: 1,
showOnlyImportant: 1,
showOnlyConfident: 1,
showOnlyNoise: 1,
showOnlyComparable: 1,
replicates: 1,
},
['filter'], ['filter'],
); );
}); });
@ -299,7 +323,13 @@ test('filters that are not enabled are removed from URL params', async () => {
fireEvent.click(showImportant); fireEvent.click(showImportant);
expect(mockUpdateParams).toHaveBeenLastCalledWith( expect(mockUpdateParams).toHaveBeenLastCalledWith(
{ page: 1, showOnlyImportant: 1 }, { page: 1, showOnlyImportant: 1 },
['filter', 'showOnlyComparable', 'showOnlyConfident', 'showOnlyNoise'], [
'filter',
'showOnlyComparable',
'showOnlyConfident',
'showOnlyNoise',
'replicates',
],
); );
fireEvent.click(showImportant); fireEvent.click(showImportant);
expect(mockUpdateParams).toHaveBeenLastCalledWith({ page: 1 }, [ expect(mockUpdateParams).toHaveBeenLastCalledWith({ page: 1 }, [
@ -308,6 +338,7 @@ test('filters that are not enabled are removed from URL params', async () => {
'showOnlyImportant', 'showOnlyImportant',
'showOnlyConfident', 'showOnlyConfident',
'showOnlyNoise', 'showOnlyNoise',
'replicates',
]); ]);
}); });

Просмотреть файл

@ -624,6 +624,7 @@ class PerformanceSummary(generics.ListAPIView):
no_subtests = query_params.validated_data['no_subtests'] no_subtests = query_params.validated_data['no_subtests']
all_data = query_params.validated_data['all_data'] all_data = query_params.validated_data['all_data']
no_retriggers = query_params.validated_data['no_retriggers'] no_retriggers = query_params.validated_data['no_retriggers']
replicates = query_params.validated_data['replicates']
signature_data = PerformanceSignature.objects.select_related( signature_data = PerformanceSignature.objects.select_related(
'framework', 'repository', 'platform', 'push', 'job' 'framework', 'repository', 'platform', 'push', 'job'
@ -707,10 +708,23 @@ class PerformanceSummary(generics.ListAPIView):
else: else:
grouped_values = defaultdict(list) grouped_values = defaultdict(list)
grouped_job_ids = defaultdict(list) grouped_job_ids = defaultdict(list)
for signature_id, value, job_id in data.values_list('signature_id', 'value', 'job_id'): if replicates:
if value is not None: for signature_id, value, job_id, replicate_value in data.values_list(
grouped_values[signature_id].append(value) 'signature_id', 'value', 'job_id', 'performancedatumreplicate__value'
grouped_job_ids[signature_id].append(job_id) ):
if replicate_value is not None:
grouped_values[signature_id].append(replicate_value)
grouped_job_ids[signature_id].append(job_id)
elif value is not None:
grouped_values[signature_id].append(value)
grouped_job_ids[signature_id].append(job_id)
else:
for signature_id, value, job_id in data.values_list(
'signature_id', 'value', 'job_id'
):
if value is not None:
grouped_values[signature_id].append(value)
grouped_job_ids[signature_id].append(job_id)
# name field is created in the serializer # name field is created in the serializer
for item in self.queryset: for item in self.queryset:

Просмотреть файл

@ -316,6 +316,7 @@ class PerformanceQueryParamsSerializer(serializers.Serializer):
signature = serializers.CharField(required=False, allow_null=True, default=None) signature = serializers.CharField(required=False, allow_null=True, default=None)
no_subtests = serializers.BooleanField(required=False) no_subtests = serializers.BooleanField(required=False)
all_data = OptionalBooleanField() all_data = OptionalBooleanField()
replicates = OptionalBooleanField()
no_retriggers = OptionalBooleanField() no_retriggers = OptionalBooleanField()
def validate(self, data): def validate(self, data):

Просмотреть файл

@ -603,6 +603,10 @@ li.pagination-active.active > button {
margin-bottom: 15px; margin-bottom: 15px;
} }
.download-json-container .btn {
margin-left: 5px;
}
.download-json-container .download-button { .download-json-container .download-button {
background-color: transparent !important; background-color: transparent !important;
color: #1f7d8e !important; color: #1f7d8e !important;

Просмотреть файл

@ -29,6 +29,7 @@ export default class CompareTableControls extends React.Component {
showImportant: convertParams(this.validated, 'showOnlyImportant'), showImportant: convertParams(this.validated, 'showOnlyImportant'),
hideUncertain: convertParams(this.validated, 'showOnlyConfident'), hideUncertain: convertParams(this.validated, 'showOnlyConfident'),
showNoise: convertParams(this.validated, 'showOnlyNoise'), showNoise: convertParams(this.validated, 'showOnlyNoise'),
useReplicates: convertParams(this.validated, 'replicates'),
results: new Map(), results: new Map(),
filteredText: this.getDefaultFilterText(this.validated), filteredText: this.getDefaultFilterText(this.validated),
showRetriggerModal: false, showRetriggerModal: false,
@ -49,6 +50,9 @@ export default class CompareTableControls extends React.Component {
const params = parseQueryParams(location.search); const params = parseQueryParams(location.search);
const prevParams = parseQueryParams(prevProps.location.search); const prevParams = parseQueryParams(prevProps.location.search);
if (params.replicates !== prevParams.replicates) {
window.location.reload(false);
}
if (prevState.countPages !== countPages) { if (prevState.countPages !== countPages) {
this.setState({ totalPagesList: this.generatePages(countPages) }); this.setState({ totalPagesList: this.generatePages(countPages) });
} }
@ -80,6 +84,13 @@ export default class CompareTableControls extends React.Component {
); );
}; };
updateReplicates = () => {
this.setState(
(prevState) => ({ useReplicates: !prevState.useReplicates }),
() => this.updateUrlParams(),
);
};
filterResult = (testName, result) => { filterResult = (testName, result) => {
const { const {
filteredText, filteredText,
@ -183,6 +194,7 @@ export default class CompareTableControls extends React.Component {
showImportant, showImportant,
hideUncertain, hideUncertain,
showNoise, showNoise,
useReplicates,
page, page,
} = this.state; } = this.state;
const compareURLParams = {}; const compareURLParams = {};
@ -203,6 +215,9 @@ export default class CompareTableControls extends React.Component {
if (showNoise) compareURLParams.showOnlyNoise = 1; if (showNoise) compareURLParams.showOnlyNoise = 1;
else paramsToRemove.push('showOnlyNoise'); else paramsToRemove.push('showOnlyNoise');
if (useReplicates) compareURLParams.replicates = 1;
else paramsToRemove.push('replicates');
compareURLParams.page = page; compareURLParams.page = page;
updateParams(compareURLParams, paramsToRemove); updateParams(compareURLParams, paramsToRemove);
}; };
@ -265,6 +280,7 @@ export default class CompareTableControls extends React.Component {
hideUncertain, hideUncertain,
showImportant, showImportant,
showNoise, showNoise,
useReplicates,
results, results,
showRetriggerModal, showRetriggerModal,
currentRetriggerRow, currentRetriggerRow,
@ -304,6 +320,8 @@ export default class CompareTableControls extends React.Component {
const viewablePagesList = this.getCurrentPages(); const viewablePagesList = this.getCurrentPages();
const hasMorePages = () => viewablePagesList.length > 0 && countPages !== 1; const hasMorePages = () => viewablePagesList.length > 0 && countPages !== 1;
const hasTryProject = () =>
validated.newProject === 'try' || validated.originalProject === 'try';
const formattedJSONData = this.formatDownloadData(compareResults); const formattedJSONData = this.formatDownloadData(compareResults);
@ -337,8 +355,19 @@ export default class CompareTableControls extends React.Component {
</Row> </Row>
) )
: null} : null}
<div className="download-json-container"> <div className="download-json-container">
{hasTryProject() && (
<Button
color="darker-info"
outline
className="btn"
type="button"
onClick={this.updateReplicates}
active={useReplicates}
>
Use replicates (try-only)
</Button>
)}
{formattedJSONData.length > 0 && ( {formattedJSONData.length > 0 && (
<Button <Button
className="btn download-button" className="btn download-button"

Просмотреть файл

@ -27,6 +27,7 @@ class CompareView extends React.PureComponent {
framework, framework,
interval, interval,
no_subtests: true, no_subtests: true,
replicates: false,
}); });
getQueryParams = (timeRange, framework) => { getQueryParams = (timeRange, framework) => {
@ -37,6 +38,7 @@ class CompareView extends React.PureComponent {
newRevision, newRevision,
newResultSet, newResultSet,
originalResultSet, originalResultSet,
replicates,
} = this.props.validated; } = this.props.validated;
let originalParams; let originalParams;
@ -71,6 +73,12 @@ class CompareView extends React.PureComponent {
const newParams = this.queryParams(newProject, interval, framework.id); const newParams = this.queryParams(newProject, interval, framework.id);
newParams.revision = newRevision; newParams.revision = newRevision;
if (replicates) {
originalParams.replicates = true;
newParams.replicates = true;
}
return [originalParams, newParams]; return [originalParams, newParams];
}; };