This commit is contained in:
Ofir Pardo 2024-04-16 18:28:44 +03:00
parent 47fc364be7
commit b567bc9be5

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { JSONReport, JSONReportSuite, JSONReportTestResult } from 'packages/playwright-test/reporter'; import type { JSONReport, JSONReportSuite } from 'packages/playwright-test/reporter';
import type { Suite, TestCase } from '../common/test'; import type { Suite, TestCase } from '../common/test';
export type TestGroup = { export type TestGroup = {
@ -29,7 +29,7 @@ type testDetails = {
id: string, id: string,
name: string, name: string,
duration: number duration: number
} };
export function createTestGroups(projectSuite: Suite, workers: number): TestGroup[] { export function createTestGroups(projectSuite: Suite, workers: number): TestGroup[] {
// This function groups tests that can be run together. // This function groups tests that can be run together.
@ -180,23 +180,23 @@ export function filterForShardFromTimingFile(timingFile: JSONReport, shard: { to
recordedTests.forEach(group => result.add(group)); recordedTests.forEach(group => result.add(group));
// If not all test groups have recorded timing information, filter the remaining groups based on shard distribution // If not all test groups have recorded timing information, filter the remaining groups based on shard distribution
if (testDetails.length != testGroups.length) { if (testDetails.length !== testGroups.length) {
const allTestIds = testDetails.map(shardDetails => shardDetails.id); const allTestIds = testDetails.map(shardDetails => shardDetails.id);
const UnrecordedTests = testGroups.filter(group => !allTestIds.includes(group.tests[0].id)); const UnrecordedTests = testGroups.filter(group => !allTestIds.includes(group.tests[0].id));
let shardableTotal = 0; let shardableTotal = 0;
for (const group of UnrecordedTests) for (const group of UnrecordedTests)
shardableTotal += group.tests.length; shardableTotal += group.tests.length;
// Each shard gets some tests. // Each shard gets some tests.
const shardSize = Math.floor(shardableTotal / shard.total); const shardSize = Math.floor(shardableTotal / shard.total);
// First few shards get one more test each. // First few shards get one more test each.
const extraOne = shardableTotal - shardSize * shard.total; const extraOne = shardableTotal - shardSize * shard.total;
const currentShard = shard.current - 1; // Make it zero-based for calculations. const currentShard = shard.current - 1; // Make it zero-based for calculations.
const from = shardSize * currentShard + Math.min(extraOne, currentShard); const from = shardSize * currentShard + Math.min(extraOne, currentShard);
const to = from + shardSize + (currentShard < extraOne ? 1 : 0); const to = from + shardSize + (currentShard < extraOne ? 1 : 0);
let current = 0; let current = 0;
for (const group of UnrecordedTests) { for (const group of UnrecordedTests) {
// Any test group goes to the shard that contains the first test of this group. // Any test group goes to the shard that contains the first test of this group.
@ -211,7 +211,7 @@ export function filterForShardFromTimingFile(timingFile: JSONReport, shard: { to
// Extracts test details from a JSON report and returns an array of test details objects. // Extracts test details from a JSON report and returns an array of test details objects.
function getAllTestDetails(report: JSONReport) { function getAllTestDetails(report: JSONReport) {
const allTestDetails:testDetails[] = []; const allTestDetails: testDetails[] = [];
report.suites.forEach((suite: JSONReportSuite) => { report.suites.forEach((suite: JSONReportSuite) => {
getTestDetails(suite, allTestDetails); getTestDetails(suite, allTestDetails);
}); });
@ -222,9 +222,9 @@ function getAllTestDetails(report: JSONReport) {
// Recursively traverses through test suites in a JSON report and extracts test details. // Recursively traverses through test suites in a JSON report and extracts test details.
function getTestDetails(suite: JSONReportSuite, allTestDetails: testDetails[]) { function getTestDetails(suite: JSONReportSuite, allTestDetails: testDetails[]) {
if (suite.specs) { if (suite.specs) {
suite.specs.forEach((spec) => { suite.specs.forEach(spec => {
spec.tests.forEach((test) => { spec.tests.forEach(test => {
test.results.forEach((result) => { test.results.forEach(result => {
allTestDetails.push({ allTestDetails.push({
id: spec.id, id: spec.id,
name: spec.title, name: spec.title,
@ -236,7 +236,7 @@ function getTestDetails(suite: JSONReportSuite, allTestDetails: testDetails[]) {
} }
if (suite.suites) { if (suite.suites) {
suite.suites.forEach((subSuite) => { suite.suites.forEach(subSuite => {
getTestDetails(subSuite, allTestDetails); getTestDetails(subSuite, allTestDetails);
}); });
} }
@ -245,13 +245,13 @@ function getTestDetails(suite: JSONReportSuite, allTestDetails: testDetails[]) {
function mapTestDetailsToShards(testDetails: testDetails[], shard: {total: number, current: number}) { function mapTestDetailsToShards(testDetails: testDetails[], shard: {total: number, current: number}) {
testDetails.sort((a, b) => b.duration - a.duration); testDetails.sort((a, b) => b.duration - a.duration);
const result:testDetails[][] = Array.from({ length: shard.total }, () => []); const result: testDetails[][] = Array.from({ length: shard.total }, () => []);
const sums = Array(shard.total).fill(0); const sums = Array(shard.total).fill(0);
// Distribute tests to shards based on their durations // Distribute tests to shards based on their durations
for (let testDetailsObj of testDetails) { for (const testDetailsObj of testDetails) {
// Find the shard with the smallest total duration and assign the test to that shard // Find the shard with the smallest total duration and assign the test to that shard
let minIndex = sums.indexOf(Math.min(...sums)); const minIndex = sums.indexOf(Math.min(...sums));
result[minIndex].push(testDetailsObj); result[minIndex].push(testDetailsObj);
sums[minIndex] += testDetailsObj.duration; sums[minIndex] += testDetailsObj.duration;
} }