Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Merge similar try jobs when possible #31347

Merged
merged 5 commits into from
Feb 16, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 71 additions & 12 deletions python/servo/try_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
# option. This file may not be copied, modified, or distributed
# except according to those terms.

from __future__ import annotations

import json
import sys
from typing import Optional
from typing import ClassVar, List, Optional
import unittest
import logging

Expand Down Expand Up @@ -40,10 +42,10 @@ def to_string(self):


class Workflow(str, Enum):
LINUX = 'linux'
MACOS = 'macos'
WINDOWS = 'windows'
ANDROID = 'android'
LINUX = "linux"
MACOS = "macos"
WINDOWS = "windows"
ANDROID = "android"


@dataclass
Expand All @@ -54,6 +56,20 @@ class JobConfig(object):
profile: str = "release"
unit_tests: bool = False
wpt_tests_to_run: str = ""
# These are the fields that must match in between two JobConfigs for them to be able to be
# merged. If you modify any of the fields above, make sure to update this line as well.
merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_tests_to_run']

def merge(self, other: JobConfig) -> bool:
"""Try to merge another job with this job. Returns True if merging is successful
or False if not. If merging is successful this job will be modified."""
for field in self.merge_compatibility_fields:
if getattr(self, field) != getattr(other, field):
return False

self.wpt_layout |= other.wpt_layout
self.unit_tests |= other.unit_tests
return True


def handle_preset(s: str) -> Optional[JobConfig]:
Expand All @@ -68,15 +84,15 @@ def handle_preset(s: str) -> Optional[JobConfig]:
elif s in ["wpt", "linux-wpt"]:
return JobConfig("Linux WPT", Workflow.LINUX, unit_tests=True, wpt_layout=Layout.all())
elif s in ["wpt-2013", "linux-wpt-2013"]:
return JobConfig("Linux WPT legacy-layout", Workflow.LINUX, wpt_layout=Layout.layout2013)
return JobConfig("Linux WPT", Workflow.LINUX, wpt_layout=Layout.layout2013)
sagudev marked this conversation as resolved.
Show resolved Hide resolved
elif s in ["wpt-2020", "linux-wpt-2020"]:
return JobConfig("Linux WPT layout-2020", Workflow.LINUX, wpt_layout=Layout.layout2020)
return JobConfig("Linux WPT", Workflow.LINUX, wpt_layout=Layout.layout2020)
elif s in ["mac-wpt", "wpt-mac"]:
return JobConfig("MacOS WPT", Workflow.MACOS, wpt_layout=Layout.all())
elif s == "mac-wpt-2013":
return JobConfig("MacOS WPT legacy-layout", Workflow.MACOS, wpt_layout=Layout.layout2013)
return JobConfig("MacOS WPT", Workflow.MACOS, wpt_layout=Layout.layout2013)
elif s == "mac-wpt-2020":
return JobConfig("MacOS WPT layout-2020", Workflow.MACOS, wpt_layout=Layout.layout2020)
return JobConfig("MacOS WPT", Workflow.MACOS, wpt_layout=Layout.layout2020)
elif s == "android":
return JobConfig("Android", Workflow.ANDROID)
elif s == "webgpu":
Expand Down Expand Up @@ -122,11 +138,17 @@ def parse(self, input: str):
words.extend(["linux-wpt", "macos", "windows", "android"])
continue # skip over keyword

preset = handle_preset(word)
if preset is None:
job = handle_preset(word)
if job is None:
print(f"Ignoring unknown preset {word}")
else:
self.matrix.append(preset)
self.add_or_merge_job_to_matrix(job)

def add_or_merge_job_to_matrix(self, job: JobConfig):
for existing_job in self.matrix:
if existing_job.merge(job):
return
self.matrix.append(job)

def to_json(self, **kwargs) -> str:
return json.dumps(self, cls=Encoder, **kwargs)
Expand Down Expand Up @@ -192,6 +214,43 @@ def test_empty(self):
}
]})

def test_job_merging(self):
self.assertDictEqual(json.loads(Config("wpt-2020 wpt-2013").to_json()),
{'fail_fast': False,
'matrix': [{
'name': 'Linux WPT',
'profile': 'release',
'unit_tests': False,
'workflow': 'linux',
'wpt_layout': 'all',
'wpt_tests_to_run': ''
}]
})

# These two jobs should not be merged to one that turns on unit tests.
a = JobConfig("Linux", Workflow.LINUX, unit_tests=True)
b = JobConfig("Linux", Workflow.LINUX, unit_tests=False)
self.assertTrue(a.merge(b))
self.assertEqual(a, JobConfig("Linux", Workflow.LINUX, unit_tests=True))
sagudev marked this conversation as resolved.
Show resolved Hide resolved

# These two jobs should not be mergable to one that turns on unit tests.
a = JobConfig("Linux", Workflow.LINUX, unit_tests=True)
b = JobConfig("Linux", Workflow.MACOS, unit_tests=True)
mrobinson marked this conversation as resolved.
Show resolved Hide resolved
self.assertFalse(a.merge(b))
self.assertEqual(a, JobConfig("Linux", Workflow.LINUX, unit_tests=True))
sagudev marked this conversation as resolved.
Show resolved Hide resolved

# Ditto.
sagudev marked this conversation as resolved.
Show resolved Hide resolved
a = JobConfig("Linux", Workflow.LINUX, unit_tests=True)
b = JobConfig("Linux", Workflow.LINUX, unit_tests=True, profile="production")
self.assertFalse(a.merge(b))
self.assertEqual(a, JobConfig("Linux", Workflow.LINUX, unit_tests=True))

# Ditto.
mrobinson marked this conversation as resolved.
Show resolved Hide resolved
a = JobConfig("Linux", Workflow.LINUX, unit_tests=True)
b = JobConfig("Linux", Workflow.LINUX, unit_tests=True, wpt_tests_to_run="/css")
self.assertFalse(a.merge(b))
self.assertEqual(a, JobConfig("Linux", Workflow.LINUX, unit_tests=True))

def test_full(self):
self.assertDictEqual(json.loads(Config("linux-wpt macos windows android").to_json()),
json.loads(Config("").to_json()))
Expand Down