Merge ~bryce/ubuntu-helpers:add-trigger-class into ~ubuntu-server/+git/ubuntu-helpers:main

Proposed by Bryce Harrington
Status: Merged
Merge reported by: Bryce Harrington
Merged at revision: b8b81821d61f040f94618b3bf65b682224256d43
Proposed branch: ~bryce/ubuntu-helpers:add-trigger-class
Merge into: ~ubuntu-server/+git/ubuntu-helpers:main
Diff against target: 115 lines (+83/-7)
2 files modified
cpaelzer/lp-test-ppa (+46/-7)
cpaelzer/smoketest_lp-test-ppa (+37/-0)
Reviewer Review Type Date Requested Status
Christian Ehrhardt  Approve
Canonical Server Pending
Canonical Server Reporter Pending
Review via email: mp+426849@code.launchpad.net

Description of the change

This adds a Trigger class and cleans up the regex used to extract triggers.
The new additions aren't actually used in the code, but I've also included a super simple smoketest to check the functionality.

I've also been running it against a PPA as a real world test.

    $ time lp-test-ppa ppa:sergiodj/sssd-merge --release kinetic
    real 0m15.545s
    user 0m0.674s
    sys 0m0.109s

I've been hoping to find ways to make the code run quicker, however the performance is pretty much dominated by network I/O.

To post a comment you must log in.
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

# ack to add some testing, no matter how trivial - A few things here though

1. shouldn't it have a list of expected results to check?

2. Instead of just being an extra script, couldn't it (mabye not in this PR, but later) be a test in whatever python testing framework you wanted to use for the final thing?

3. The artifacts are not always reachable forever, please add a check if the fetch worked and if e.g. the expected number of lines came back. If not skip the asserts that should follow (see #1)

# Then on "Simplify regex for triggers"

1. It is fine to reduce the potential mass parsing, but could we be more explicit by using
header_split = self.log.split(": @@@@@@@@@@@@@@@@@@@@ test bed setup", 1)
instead of
header_split = self.log.split(": @@@@@@@@@@@@@@@@@@@@", 1)
Then also the <2 makes more sense. Because otherwise you might split latter sections, the split will be >=2 but still might not contain what you expect.

2. could we define and compile the regex for ADT_TEST_TRIGGERS only if we passed the header split
That reads more like first header split, then regex to parse.
In addition for the (gladly unlikely) case that the header is bad you won't even compile the regex

# Trigger class

I like that this aligns things with each other, but as asked above comparing against expected output in the test - especially using this split package/version/arch/series as well as the stringified autopkgtest_url would help to test that.

# Finally
While all this is nice, I assume there will be changes that then use Triggers right?
Because otherwise while nice these changes will end up feeling odd.
So let me just ask for confirmation there will be users of this other than the test?

TL;DR:
Great, a few small further enhancements/changes requested.
My asks are all simple, feel free to merge yourself without re-review once you addressed them.

review: Needs Fixing
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

I've found this on testing against a PPA that exists but has no tests nor builds run yet.

$ lp-test-ppa ppa:paelzer/kinetic-librabbitmq --release kinetic
Tests for PPA kinetic-librabbitmq
---- ---- ---- ----
Release: kinetic
Sources:
  SRC: librabbitmq @ 0.11.0-1~kineticppa1 - Pending
Triggers on published Sources:
Error: Could not retrieve results from http://autopkgtest.ubuntu.com/results/autopkgtest-kinetic-paelzer-kinetic-librabbitmq/?format=plain: HTTP Error 401: Unauthorized
Results: (none yet at http://autopkgtest.ubuntu.com/results/autopkgtest-kinetic-paelzer-kinetic-librabbitmq/?format=plain)
Running: (none)
Waiting: (none)

Maybe while updating the triggers we should harden it to not yield
"Error: Could not retrieve results from http://autopkgtest.ubuntu.com/results/autopkgtest-kinetic-paelzer-kinetic-librabbitmq/?format=plain: HTTP Error 401: Unauthorized"

Instead if should either say nothing or something like "no tests/builds yet"

Revision history for this message
Bryce Harrington (bryce) wrote :
Download full text (3.9 KiB)

On Thu, Jul 14, 2022 at 07:51:08AM -0000, Christian Ehrhardt  wrote:
> Review: Needs Fixing
>
> # ack to add some testing, no matter how trivial - A few things here though
>
> 1. shouldn't it have a list of expected results to check?
>
> 2. Instead of just being an extra script, couldn't it (mabye not in this PR, but later) be a test in whatever python testing framework you wanted to use for the final thing?
>
> 3. The artifacts are not always reachable forever, please add a check if the fetch worked and if e.g. the expected number of lines came back. If not skip the asserts that should follow (see #1)

Yep, the intent is this will evolve into a test case in ppa-dev-tools
(which is already set up with a pytest-based testsuite). I just wanted
something quick and dirty to exercise the code without putting too much
time in. I do plan on evolving this into a proper test, and tackling
all those points.

> # Then on "Simplify regex for triggers"
>
> 1. It is fine to reduce the potential mass parsing, but could we be more explicit by using
> header_split = self.log.split(": @@@@@@@@@@@@@@@@@@@@ test bed setup", 1)
> instead of
> header_split = self.log.split(": @@@@@@@@@@@@@@@@@@@@", 1)
> Then also the <2 makes more sense. Because otherwise you might split latter sections, the split will be >=2 but still might not contain what you expect.
>

Yeah, what might be better would be to go ahead and split all the
sections and store them into a list. The section names are unforunately
not unique (i.e. each test starts with 'test bed setup') so can't be
like an OrderedDict, but it could be a list of tuples.

In any case, the header is always the first section, which is why I
didn't reference the second section's 'test bed setup' name.

But yeah, having some sort of retained data structure for all the
sections could be useful if we ever want to do per-subtest analysis.
I'll followup this MP with another to do this.

> 2. could we define and compile the regex for ADT_TEST_TRIGGERS only if we passed the header split
> That reads more like first header split, then regex to parse.
> In addition for the (gladly unlikely) case that the header is bad you won't even compile the regex

In fact I think the regex compilation can be moved to a static class
variable; typically regex compilation only needs to be done once at
program startup. So yeah, I'll move this to a better location.

> # Trigger class
>
> I like that this aligns things with each other, but as asked above comparing against expected output in the test - especially using this split package/version/arch/series as well as the stringified autopkgtest_url would help to test that.
>

Agreed.

> # Finally
> While all this is nice, I assume there will be changes that then use Triggers right?
> Because otherwise while nice these changes will end up feeling odd.
> So let me just ask for confirmation there will be users of this other than the test?

Yes, this MP is just the first part of a larger effort for triggers, it
was just the amount I could complete in the time I had for it
yesterday. The next step is refactoring the code that is operating on
"package/version" trigger strings to instead operat...

Read more...

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

#1
ok on quick'n-dirty test for now, as you confirmed this will merge into a real test (framework).

#2 ok to do this in a follow up as you suggested - thanks

#3 ok as well

So land it on your terms and take not of the identified actions to be done later.
I'm looking forward to the next few PRs :-)

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cpaelzer/lp-test-ppa b/cpaelzer/lp-test-ppa
2index 63d95d1..08dbfe3 100755
3--- a/cpaelzer/lp-test-ppa
4+++ b/cpaelzer/lp-test-ppa
5@@ -87,6 +87,24 @@ class Subtest:
6 return Subtest.VALUES[self.status]
7
8
9+class Trigger:
10+ def __init__(self, package, version, arch, series):
11+ self.package = package
12+ self.version = version
13+ self.arch = arch
14+ self.series = series
15+
16+ def __repr__(self):
17+ return f"{self.package}/{self.version}"
18+
19+ def __str__(self):
20+ return f"{self.package}/{self.version}"
21+
22+ @property
23+ def autopkgtest_url(self):
24+ return f"{URL_AUTOPKGTEST}/o/{self.package}/{self.series}/{self.arch}"
25+ pass
26+
27 class Result:
28 VALUES = {
29 'PASS': "✅",
30@@ -149,14 +167,35 @@ class Result:
31 :rtype: list[str]
32 :returns: List of package/version triggers.
33 """
34- res_trigger = re.search("--env=ADT_TEST_TRIGGERS=.* -- ", self.log)
35- if not res_trigger:
36+ regex_triggers = re.compile(r'--env=ADT_TEST_TRIGGERS=(.*?) -- ')
37+ header_split = self.log.split(": @@@@@@@@@@@@@@@@@@@@", 1)
38+ if len(header_split) < 2:
39+ return []
40+ m = re.search(regex_triggers, header_split[0])
41+ if not m:
42+ return []
43+
44+ return m.group(1).strip("'").split()
45+
46+ @lru_cache
47+ def get_triggers(self, name=None):
48+ """Provides list of Triggers that were used to create this Result
49+
50+ This returns the set of Triggers used to create the Result, as
51+ recorded in the test log. Each trigger identifies a
52+ package/version pair corresponding to source packages to use
53+ from the proposed archive (instead of from the release archive).
54+
55+ :param str name: If defined, only return triggers starting with this name.
56+ :rtype: Iterator[Trigger]
57+ :returns: Triggers used to generate this Result, if any, or an empty list
58+ """
59+ if not self.triggers:
60 return []
61- res_trigger = res_trigger.group(0)
62- res_trigger = res_trigger[len("--env=ADT_TEST_TRIGGERS="):]
63- res_trigger = res_trigger[:res_trigger.find(" -- ")]
64- res_trigger = res_trigger.strip("'")
65- return res_trigger.split()
66+
67+ for t in self.triggers:
68+ package, version = t.split('/', 1)
69+ yield Trigger(package, version, arch=self.arch, series=self.series)
70
71 @lru_cache
72 def get_subtests(self, name=None):
73diff --git a/cpaelzer/smoketest_lp-test-ppa b/cpaelzer/smoketest_lp-test-ppa
74new file mode 100755
75index 0000000..d8b2965
76--- /dev/null
77+++ b/cpaelzer/smoketest_lp-test-ppa
78@@ -0,0 +1,37 @@
79+#!/usr/bin/python3
80+
81+# Author: Bryce Harrington <bryce@canonical.com>
82+#
83+# Copyright (C) 2021 Bryce W. Harrington
84+#
85+# Released under GNU GPLv2 or later, read the file 'LICENSE.GPLv2+' for
86+# more information.
87+
88+import os
89+import sys
90+import types
91+
92+import importlib.machinery
93+
94+SCRIPT_NAME = 'lp-test-ppa'
95+BASE_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
96+scripts_path = os.path.join(BASE_PATH, 'cpaelzer')
97+sys.path.insert(0, BASE_PATH)
98+
99+script_path = os.path.join(scripts_path, SCRIPT_NAME)
100+loader = importlib.machinery.SourceFileLoader(SCRIPT_NAME, script_path)
101+script = types.ModuleType(loader.name)
102+loader.exec_module(script)
103+
104+autopkgtest_log_urls = [
105+ "https://autopkgtest.ubuntu.com/results/autopkgtest-kinetic-sergiodj-sssd-merge/kinetic/arm64/s/sssd/20220614_220046_4c037@/log.gz",
106+ "https://autopkgtest.ubuntu.com/results/autopkgtest-kinetic/kinetic/arm64/a/apache2/20220513_233749_05356@/log.gz",
107+ "https://autopkgtest.ubuntu.com/results/autopkgtest-kinetic/kinetic/amd64/u/ubuntu-image/20220523_161504_bf590@/log.gz"
108+ ]
109+
110+for url in autopkgtest_log_urls:
111+ result = script.Result(url, None, None, None, None)
112+ print(url)
113+ print(list(result.get_triggers()))
114+ print()
115+

Subscribers

People subscribed via source and target branches