|
5 | 5 | from django.urls import reverse |
6 | 6 |
|
7 | 7 | from treeherder.model.models import Job |
8 | | -from treeherder.perf.models import PerformanceDatum, PerformanceDatumReplicate |
| 8 | +from treeherder.perf.models import ( |
| 9 | + PerfCompareMwuCache, |
| 10 | + PerformanceDatum, |
| 11 | + PerformanceDatumReplicate, |
| 12 | +) |
9 | 13 | from treeherder.webapp.api import perfcompare_utils |
10 | 14 |
|
11 | 15 | pytestmark = pytest.mark.perf |
@@ -1396,3 +1400,341 @@ def test_perfcompare_results_with_silverman_kde_enabled( |
1396 | 1400 |
|
1397 | 1401 | # Verify warnings is a list (may be empty or contain warnings) |
1398 | 1402 | assert isinstance(result_with_kde["silverman_warnings"], list) |
| 1403 | + |
| 1404 | + |
| 1405 | +def test_mwu_cache_hit_returns_cached_response( |
| 1406 | + client, |
| 1407 | + create_signature, |
| 1408 | + create_perf_datum, |
| 1409 | + test_perf_signature, |
| 1410 | + test_repository, |
| 1411 | + try_repository, |
| 1412 | + eleven_jobs_stored, |
| 1413 | + test_perfcomp_push, |
| 1414 | + test_perfcomp_push_2, |
| 1415 | + test_linux_platform, |
| 1416 | + test_option_collection, |
| 1417 | +): |
| 1418 | + # Given: base and new signatures with MWU-compatible perf data |
| 1419 | + perf_jobs = Job.objects.filter(pk__in=range(1, 11)).order_by("push__time").all() |
| 1420 | + |
| 1421 | + test_perfcomp_push.time = FOUR_DAYS_AGO |
| 1422 | + test_perfcomp_push.repository = try_repository |
| 1423 | + test_perfcomp_push.save() |
| 1424 | + test_perfcomp_push_2.time = datetime.datetime.now() |
| 1425 | + test_perfcomp_push_2.save() |
| 1426 | + |
| 1427 | + suite = "a11yr" |
| 1428 | + test = "dhtml.html" |
| 1429 | + extra_options = "e10s fission stylo webrender" |
| 1430 | + measurement_unit = "ms" |
| 1431 | + |
| 1432 | + base_sig = create_signature( |
| 1433 | + signature_hash=(20 * "c1"), |
| 1434 | + extra_options=extra_options, |
| 1435 | + platform=test_linux_platform, |
| 1436 | + measurement_unit=measurement_unit, |
| 1437 | + suite=suite, |
| 1438 | + test=test, |
| 1439 | + test_perf_signature=test_perf_signature, |
| 1440 | + repository=try_repository, |
| 1441 | + application="firefox", |
| 1442 | + ) |
| 1443 | + base_perf_data_values = [32.4, 33.1, 31.8] |
| 1444 | + |
| 1445 | + for i, value in enumerate(base_perf_data_values): |
| 1446 | + job = perf_jobs[i] |
| 1447 | + job.push = test_perfcomp_push |
| 1448 | + job.save() |
| 1449 | + perf_datum = PerformanceDatum.objects.create( |
| 1450 | + value=value, |
| 1451 | + push_timestamp=job.push.time, |
| 1452 | + job=job, |
| 1453 | + push=job.push, |
| 1454 | + repository=try_repository, |
| 1455 | + signature=base_sig, |
| 1456 | + ) |
| 1457 | + perf_datum.push.time = job.push.time |
| 1458 | + perf_datum.push.save() |
| 1459 | + |
| 1460 | + new_sig = create_signature( |
| 1461 | + signature_hash=(20 * "c2"), |
| 1462 | + extra_options=extra_options, |
| 1463 | + platform=test_linux_platform, |
| 1464 | + measurement_unit=measurement_unit, |
| 1465 | + suite=suite, |
| 1466 | + test=test, |
| 1467 | + test_perf_signature=test_perf_signature, |
| 1468 | + repository=test_repository, |
| 1469 | + application="firefox", |
| 1470 | + ) |
| 1471 | + new_perf_data_values = [40.2, 41.5, 39.8] |
| 1472 | + |
| 1473 | + for i, value in enumerate(new_perf_data_values): |
| 1474 | + job = perf_jobs[i + 3] |
| 1475 | + job.push = test_perfcomp_push_2 |
| 1476 | + job.save() |
| 1477 | + perf_datum = PerformanceDatum.objects.create( |
| 1478 | + value=value, |
| 1479 | + push_timestamp=job.push.time, |
| 1480 | + job=job, |
| 1481 | + push=job.push, |
| 1482 | + repository=test_repository, |
| 1483 | + signature=new_sig, |
| 1484 | + ) |
| 1485 | + perf_datum.push.time = job.push.time |
| 1486 | + perf_datum.push.save() |
| 1487 | + |
| 1488 | + assert PerfCompareMwuCache.objects.count() == 0 |
| 1489 | + |
| 1490 | + query_params = ( |
| 1491 | + f"?base_repository={try_repository.name}&new_repository={test_repository.name}" |
| 1492 | + f"&new_revision={test_perfcomp_push_2.revision}" |
| 1493 | + f"&framework={test_perf_signature.framework_id}" |
| 1494 | + f"&interval=604800&no_subtests=true&test_version=mann-whitney-u" |
| 1495 | + ) |
| 1496 | + |
| 1497 | + # When: the same MWU comparison is requested twice |
| 1498 | + first_response = client.get(reverse("perfcompare-results") + query_params) |
| 1499 | + assert first_response.status_code == 200 |
| 1500 | + |
| 1501 | + second_response = client.get(reverse("perfcompare-results") + query_params) |
| 1502 | + assert second_response.status_code == 200 |
| 1503 | + |
| 1504 | + # Then: both responses are identical and a single cache row was created |
| 1505 | + assert first_response.json() == second_response.json() |
| 1506 | + assert PerfCompareMwuCache.objects.count() == 1 |
| 1507 | + |
| 1508 | + |
| 1509 | +def test_mwu_cache_different_params_produce_different_cache_keys( |
| 1510 | + client, |
| 1511 | + create_signature, |
| 1512 | + create_perf_datum, |
| 1513 | + test_perf_signature, |
| 1514 | + test_repository, |
| 1515 | + try_repository, |
| 1516 | + eleven_jobs_stored, |
| 1517 | + test_perfcomp_push, |
| 1518 | + test_perfcomp_push_2, |
| 1519 | + test_linux_platform, |
| 1520 | + test_option_collection, |
| 1521 | +): |
| 1522 | + # Given: base and new signatures with enough data for MWU analysis |
| 1523 | + perf_jobs = Job.objects.filter(pk__in=range(1, 11)).order_by("push__time").all() |
| 1524 | + |
| 1525 | + test_perfcomp_push.time = FOUR_DAYS_AGO |
| 1526 | + test_perfcomp_push.repository = try_repository |
| 1527 | + test_perfcomp_push.save() |
| 1528 | + test_perfcomp_push_2.time = datetime.datetime.now() |
| 1529 | + test_perfcomp_push_2.save() |
| 1530 | + |
| 1531 | + suite = "a11yr" |
| 1532 | + test = "dhtml.html" |
| 1533 | + extra_options = "e10s fission stylo webrender" |
| 1534 | + measurement_unit = "ms" |
| 1535 | + |
| 1536 | + base_sig = create_signature( |
| 1537 | + signature_hash=(20 * "d1"), |
| 1538 | + extra_options=extra_options, |
| 1539 | + platform=test_linux_platform, |
| 1540 | + measurement_unit=measurement_unit, |
| 1541 | + suite=suite, |
| 1542 | + test=test, |
| 1543 | + test_perf_signature=test_perf_signature, |
| 1544 | + repository=try_repository, |
| 1545 | + ) |
| 1546 | + base_perf_data_values = [100.0, 105.0, 102.0] |
| 1547 | + |
| 1548 | + for i, value in enumerate(base_perf_data_values): |
| 1549 | + job = perf_jobs[i] |
| 1550 | + job.push = test_perfcomp_push |
| 1551 | + job.save() |
| 1552 | + perf_datum = PerformanceDatum.objects.create( |
| 1553 | + value=value, |
| 1554 | + push_timestamp=job.push.time, |
| 1555 | + job=job, |
| 1556 | + push=job.push, |
| 1557 | + repository=try_repository, |
| 1558 | + signature=base_sig, |
| 1559 | + ) |
| 1560 | + perf_datum.push.time = job.push.time |
| 1561 | + perf_datum.push.save() |
| 1562 | + |
| 1563 | + new_sig = create_signature( |
| 1564 | + signature_hash=(20 * "d2"), |
| 1565 | + extra_options=extra_options, |
| 1566 | + platform=test_linux_platform, |
| 1567 | + measurement_unit=measurement_unit, |
| 1568 | + suite=suite, |
| 1569 | + test=test, |
| 1570 | + test_perf_signature=test_perf_signature, |
| 1571 | + repository=test_repository, |
| 1572 | + ) |
| 1573 | + new_perf_data_values = [110.0, 115.0, 112.0] |
| 1574 | + |
| 1575 | + for i, value in enumerate(new_perf_data_values): |
| 1576 | + job = perf_jobs[i + 3] |
| 1577 | + job.push = test_perfcomp_push_2 |
| 1578 | + job.save() |
| 1579 | + perf_datum = PerformanceDatum.objects.create( |
| 1580 | + value=value, |
| 1581 | + push_timestamp=job.push.time, |
| 1582 | + job=job, |
| 1583 | + push=job.push, |
| 1584 | + repository=test_repository, |
| 1585 | + signature=new_sig, |
| 1586 | + ) |
| 1587 | + perf_datum.push.time = job.push.time |
| 1588 | + perf_datum.push.save() |
| 1589 | + |
| 1590 | + base_query = ( |
| 1591 | + f"?base_repository={try_repository.name}&new_repository={test_repository.name}" |
| 1592 | + f"&new_revision={test_perfcomp_push_2.revision}" |
| 1593 | + f"&framework={test_perf_signature.framework_id}" |
| 1594 | + f"&interval=604800&no_subtests=true&test_version=mann-whitney-u" |
| 1595 | + ) |
| 1596 | + |
| 1597 | + # Given: no cache entries exist yet |
| 1598 | + assert PerfCompareMwuCache.objects.count() == 0 |
| 1599 | + |
| 1600 | + # When: two MWU requests are made with different enable_silverman_kde parameters |
| 1601 | + query_without_kde = base_query |
| 1602 | + first_response = client.get(reverse("perfcompare-results") + query_without_kde) |
| 1603 | + assert first_response.status_code == 200 |
| 1604 | + |
| 1605 | + query_with_kde = base_query + "&enable_silverman_kde=true&replicates=true" |
| 1606 | + second_response = client.get(reverse("perfcompare-results") + query_with_kde) |
| 1607 | + assert second_response.status_code == 200 |
| 1608 | + |
| 1609 | + # Then: two distinct cache entries exist (different hash keys) |
| 1610 | + assert PerfCompareMwuCache.objects.count() == 2 |
| 1611 | + cache_keys = set(PerfCompareMwuCache.objects.values_list("hash_key", flat=True)) |
| 1612 | + assert len(cache_keys) == 2 |
| 1613 | + |
| 1614 | + # Then: the responses differ in silverman_kde content |
| 1615 | + first_result = first_response.json()[0] |
| 1616 | + second_result = second_response.json()[0] |
| 1617 | + assert first_result["silverman_kde"] is None |
| 1618 | + assert second_result["silverman_kde"] is not None |
| 1619 | + |
| 1620 | + |
| 1621 | +def test_mwu_cache_recalculates_after_data_change( |
| 1622 | + client, |
| 1623 | + create_signature, |
| 1624 | + create_perf_datum, |
| 1625 | + test_perf_signature, |
| 1626 | + test_repository, |
| 1627 | + try_repository, |
| 1628 | + eleven_jobs_stored, |
| 1629 | + test_perfcomp_push, |
| 1630 | + test_perfcomp_push_2, |
| 1631 | + test_linux_platform, |
| 1632 | + test_option_collection, |
| 1633 | +): |
| 1634 | + # Given: base and new signatures with MWU-compatible perf data |
| 1635 | + perf_jobs = Job.objects.filter(pk__in=range(1, 11)).order_by("push__time").all() |
| 1636 | + |
| 1637 | + test_perfcomp_push.time = FOUR_DAYS_AGO |
| 1638 | + test_perfcomp_push.repository = try_repository |
| 1639 | + test_perfcomp_push.save() |
| 1640 | + test_perfcomp_push_2.time = datetime.datetime.now() |
| 1641 | + test_perfcomp_push_2.save() |
| 1642 | + |
| 1643 | + suite = "a11yr" |
| 1644 | + test = "dhtml.html" |
| 1645 | + extra_options = "e10s fission stylo webrender" |
| 1646 | + measurement_unit = "ms" |
| 1647 | + |
| 1648 | + base_sig = create_signature( |
| 1649 | + signature_hash=(20 * "e1"), |
| 1650 | + extra_options=extra_options, |
| 1651 | + platform=test_linux_platform, |
| 1652 | + measurement_unit=measurement_unit, |
| 1653 | + suite=suite, |
| 1654 | + test=test, |
| 1655 | + test_perf_signature=test_perf_signature, |
| 1656 | + repository=try_repository, |
| 1657 | + application="firefox", |
| 1658 | + ) |
| 1659 | + base_perf_data_values = [32.4, 33.1] |
| 1660 | + |
| 1661 | + for i, value in enumerate(base_perf_data_values): |
| 1662 | + job = perf_jobs[i] |
| 1663 | + job.push = test_perfcomp_push |
| 1664 | + job.save() |
| 1665 | + perf_datum = PerformanceDatum.objects.create( |
| 1666 | + value=value, |
| 1667 | + push_timestamp=job.push.time, |
| 1668 | + job=job, |
| 1669 | + push=job.push, |
| 1670 | + repository=try_repository, |
| 1671 | + signature=base_sig, |
| 1672 | + ) |
| 1673 | + perf_datum.push.time = job.push.time |
| 1674 | + perf_datum.push.save() |
| 1675 | + |
| 1676 | + new_sig = create_signature( |
| 1677 | + signature_hash=(20 * "e2"), |
| 1678 | + extra_options=extra_options, |
| 1679 | + platform=test_linux_platform, |
| 1680 | + measurement_unit=measurement_unit, |
| 1681 | + suite=suite, |
| 1682 | + test=test, |
| 1683 | + test_perf_signature=test_perf_signature, |
| 1684 | + repository=test_repository, |
| 1685 | + application="firefox", |
| 1686 | + ) |
| 1687 | + new_perf_data_values = [40.2, 41.5] |
| 1688 | + |
| 1689 | + for i, value in enumerate(new_perf_data_values): |
| 1690 | + job = perf_jobs[i + 3] |
| 1691 | + job.push = test_perfcomp_push_2 |
| 1692 | + job.save() |
| 1693 | + perf_datum = PerformanceDatum.objects.create( |
| 1694 | + value=value, |
| 1695 | + push_timestamp=job.push.time, |
| 1696 | + job=job, |
| 1697 | + push=job.push, |
| 1698 | + repository=test_repository, |
| 1699 | + signature=new_sig, |
| 1700 | + ) |
| 1701 | + perf_datum.push.time = job.push.time |
| 1702 | + perf_datum.push.save() |
| 1703 | + |
| 1704 | + query_params = ( |
| 1705 | + f"?base_repository={try_repository.name}&new_repository={test_repository.name}" |
| 1706 | + f"&new_revision={test_perfcomp_push_2.revision}" |
| 1707 | + f"&framework={test_perf_signature.framework_id}" |
| 1708 | + f"&interval=604800&no_subtests=true&test_version=mann-whitney-u" |
| 1709 | + ) |
| 1710 | + |
| 1711 | + # When: first MWU query is made, caching the result |
| 1712 | + first_response = client.get(reverse("perfcompare-results") + query_params) |
| 1713 | + assert first_response.status_code == 200 |
| 1714 | + assert PerfCompareMwuCache.objects.count() == 1 |
| 1715 | + _first_cache_key = PerfCompareMwuCache.objects.first().hash_key |
| 1716 | + |
| 1717 | + # Given: a new data point is added for the base signature (simulating a retrigger) |
| 1718 | + job = perf_jobs[2] |
| 1719 | + job.push = test_perfcomp_push |
| 1720 | + job.save() |
| 1721 | + perf_datum = PerformanceDatum.objects.create( |
| 1722 | + value=31.8, |
| 1723 | + push_timestamp=job.push.time, |
| 1724 | + job=job, |
| 1725 | + push=job.push, |
| 1726 | + repository=try_repository, |
| 1727 | + signature=base_sig, |
| 1728 | + ) |
| 1729 | + perf_datum.push.time = job.push.time |
| 1730 | + perf_datum.push.save() |
| 1731 | + |
| 1732 | + # When: the same MWU query is made again after data changed |
| 1733 | + second_response = client.get(reverse("perfcompare-results") + query_params) |
| 1734 | + assert second_response.status_code == 200 |
| 1735 | + |
| 1736 | + # Then: a second cache entry exists with a different hash key |
| 1737 | + # because the total data point count changed |
| 1738 | + assert PerfCompareMwuCache.objects.count() == 2 |
| 1739 | + cache_keys = list(PerfCompareMwuCache.objects.values_list("hash_key", flat=True)) |
| 1740 | + assert cache_keys[0] != cache_keys[1] |
0 commit comments