tests: add audio-record prompting integration tests - #17015
Conversation
There was a problem hiding this comment.
Pull request overview
Adds new spread variants to the apparmor-prompting-integration-tests suite to exercise AppArmor prompting end-to-end for the audio-record interface, including installing a helper snap and cleaning up prompting state between runs.
Changes:
- Add
audio-recordspread variants and install/connect aprompt-requesterhelper snap for those variants. - Add scripted prompting-client JSON + shell drivers for
audio-recordsingle and timespan scenarios. - Ensure prompting request/rule state is removed on restore to avoid cross-test interference.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/main/apparmor-prompting-integration-tests/task.yaml | Adds new audio-record variants, installs helper snap for those variants, and cleans prompting state on restore. |
| tests/main/apparmor-prompting-integration-tests/prompt-requester/meta/snap.yaml | Defines a helper snap used to create a process/cgroup context for ask requests. |
| tests/main/apparmor-prompting-integration-tests/prompt-requester/bin/wait-for | Helper app that stays running until a “finish” file appears (used to keep a stable PID/cgroup). |
| tests/main/apparmor-prompting-integration-tests/prompt-requester/bin/cat | Simple wrapper around /bin/cat (part of helper snap). |
| tests/main/apparmor-prompting-integration-tests/audio_record_single.sh | Drives ask requests for audio-record and validates outcomes across multiple prompts. |
| tests/main/apparmor-prompting-integration-tests/audio_record_single.json | Scripted prompting-client prompt filters + replies for the single-lifespan flow. |
| tests/main/apparmor-prompting-integration-tests/audio_record_timespan_allow.sh | Drives ask requests and validates allow-timespan then deny after expiry. |
| tests/main/apparmor-prompting-integration-tests/audio_record_timespan_allow.json | Scripted replies for allow-timespan then deny. |
| tests/main/apparmor-prompting-integration-tests/audio_record_timespan_deny.sh | Drives ask requests and validates deny-timespan then allow after expiry. |
| tests/main/apparmor-prompting-integration-tests/audio_record_timespan_deny.json | Scripted replies for deny-timespan then allow. |
|
Wed May 6 15:08:19 UTC 2026 Failures:Preparing:
Executing:
Restoring:
Skipped tests from snapd-testing-skipIf you wish to have any of the below tests run in your PR, in your PR description, add 'unskip:' followed by a copy-and-pasted list (without variants) of the below tests you wish to run (unskip plus test list must be valid yaml)
|
natibek
left a comment
There was a problem hiding this comment.
Thanks! Just one question.
| MATCH '"outcome": "allow"' < "$RESULT" | ||
|
|
||
| # Trigger a fourth request | ||
| ASK_BODY="{\"action\": \"ask\", \"interface\": \"audio-record\", \"pid\": $WAITER_SNAP_PID}" |
There was a problem hiding this comment.
Is there a concern that this fourth request is triggered close to the 10second duration of the initial response?
There was a problem hiding this comment.
I don't think so, these requests should be basically instantaneous.
5d71300 to
2dfa5ba
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #17015 +/- ##
==========================================
+ Coverage 79.07% 79.13% +0.05%
==========================================
Files 1378 1367 -11
Lines 191273 191111 -162
Branches 2465 2465
==========================================
- Hits 151247 151232 -15
+ Misses 30920 30779 -141
+ Partials 9106 9100 -6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
|
||
| echo "Run the test script as the test user" | ||
| if ! tests.session -u test exec sh -x "${TEST_DIR}/${VARIANT}.sh" "$TEST_DIR" "$TIMEOUT"; then | ||
| if ! tests.session -u test exec sh -xe "${TEST_DIR}/${VARIANT}.sh" "$TEST_DIR" "$TIMEOUT"; then |
There was a problem hiding this comment.
Not having -e confused me a lot, so it makes more sense to keep -e and instead specially handle the places in which we expect failures to occur.
|
|
||
| for name in test1.txt test2.txt test3.txt test4.txt ; do | ||
| TEST_OUTPUT="$(cat "${WRITABLE}/${name}")" | ||
| TEST_OUTPUT="$(cat "${WRITABLE}/${name}" || true)" |
There was a problem hiding this comment.
That's why we check $TEST_OUTPUT on the next line
| for i in $(seq "$TIMEOUT") ; do | ||
| if ! pgrep -af "prompting-client.scripted.*${TEST_DIR}" ; then | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| if pgrep -af "prompting-client.scripted.*${TEST_DIR}" ; then |
There was a problem hiding this comment.
This was deliberate, if this is hanging I want to see what's running.
| # expect any rules with duplicate path patterns. | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | MATCH '^[[:space:]]*1' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | NOMATCH '^[[:space:]]*2' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | grep '^[[:space:]]*1' | ||
| ! snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | grep -q '^[[:space:]]*[^1[[:space:]]]' |
| # Rules with identical path patterns are merged, so we don't expect any rules | ||
| # with duplicate path patterns. | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | MATCH '^[[:space:]]*1' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | NOMATCH '^[[:space:]]*2' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | grep '^[[:space:]]*1' | ||
| ! snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | grep -q '^[[:space:]]*[^1[[:space:]]]' |
| # Start the snap running in the background so "ask" can use its PID to look up | ||
| # its cgroup, and from that derive the snap name. | ||
| prompt-requester.wait-for "$TARGET_FILE" & | ||
| WAITER_SHELL_PID="$!" | ||
|
|
| # Start the snap running in the background so "ask" can use its PID to look up | ||
| # its cgroup, and from that derive the snap name. | ||
| prompt-requester.wait-for "$TARGET_FILE" & | ||
| WAITER_SHELL_PID="$!" | ||
|
|
| # Start the snap running in the background so "ask" can use its PID to look up | ||
| # its cgroup, and from that derive the snap name. | ||
| prompt-requester.wait-for "$TARGET_FILE" & | ||
| WAITER_SHELL_PID="$!" | ||
|
|
| # expect any rules with duplicate path patterns. | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | MATCH '^[[:space:]]*1' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | NOMATCH '^[[:space:]]*2' | ||
| snap debug api /v2/interfaces/requests/rules | jq '."result".[]."constraints"."path-pattern"' | grep "${TEST_DIR}" | uniq -c | grep '^[[:space:]]*1' |
There was a problem hiding this comment.
I think this pattern needs to be something like ^[[:space:]]*1[[:space:]]*$ to avoid matching any number starting with the digit 1.
There was a problem hiding this comment.
Though, this is a bit extreme I realize since we only send the request twice.
There was a problem hiding this comment.
That was my rationale too... I'll slot in a fix if there are any other things to change, but otherwise I don't think it's necessary. The regex would have to be this I think: ^[[:space:]]*1[[:space:]] since after the number and a space there will be other text.
| for i in $(seq "$TIMEOUT") ; do | ||
| if ! pgrep -af "prompting-client.scripted.*${TEST_DIR}" ; then | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| if pgrep -af "prompting-client.scripted.*${TEST_DIR}" ; then | ||
| echo "prompting-client.scripted still running" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
You are really doing this construct a lot, maybe worth abstracting one day, but not high priority.
There was a problem hiding this comment.
Yeah I had an abstraction via timeout and while loop but then the timeout and while commands were being caught in the pgrep lookup, causing it to never see itself as done. And pgrep -f doesn't work with binary names longer than 15 characters so I gave up and did the most direct thing. If you have ideas though I'm all ears.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bcf60fb to
9d51f3d
Compare
|
Wed May 6 20:59:20 UTC 2026 Failures:Preparing:
Executing:
Restoring:
Skipped tests from snapd-testing-skipIf you wish to have any of the below tests run in your PR, in your PR description, add 'unskip:' followed by a copy-and-pasted list (without variants) of the below tests you wish to run (unskip plus test list must be valid yaml)
|
Now that snapd and the
prompting-clientsupport prompting foraudio-record, addapparmor-prompting-integration-testsvariants to exercise it end-to-end.Lifespan "forever" is exercised thoroughly elsewhere, so use lifespan "timespan" to check rule persistence, especially since triggering prompts for
audio-recordis comparatively simple and quick to check.Also, since
audio-recordconstraints have no path/path-pattern, there's no way to namespace rules between tests, so we need to ensure they are cleaned between runs.These are inspired by the
audio-recordsmoke test, since there's no WirePlumber available in spread VMs to trigger a prompt the way it happens in practice (on resolute+).This relies on the
prompting-clientshipping this fix: canonical/prompting-client#308So in the meantime, we may need to switch this spread test to use the
latest/edgechannel of theprompting-client, and switch back tolatest/stablelater. We similarly need thelatest/edgechannel to land #16931, so it's probably worth making this switch in a dedicated PR, then landing these two PRs, then later reverting the one which switched channels.This work is tracked internally by https://warthogs.atlassian.net/browse/SNAPDENG-36765
unskip: