From b081578665a392e5ec1a1f4e8fdafe3663293820 Mon Sep 17 00:00:00 2001 From: Johannah Sprinz Date: Wed, 23 Mar 2022 23:45:48 +0100 Subject: [PATCH 1/3] Add fastboot:assert_var and adb:assert_prop actions for #2505 ```yml - actions: - adb:assert_prop: prop: "ro.ubuntu.recovery" value: "true" - actions: - fastboot:assert_var: variable: "ro.ubuntu.recovery" value: "true" ``` --- src/core/plugins/adb/plugin.js | 18 ++++++++++ src/core/plugins/adb/plugin.spec.js | 40 ++++++++++++++++++++++ src/core/plugins/fastboot/plugin.js | 21 ++++++++++++ src/core/plugins/fastboot/plugin.spec.js | 43 ++++++++++++++++++++++++ 4 files changed, 122 insertions(+) diff --git a/src/core/plugins/adb/plugin.js b/src/core/plugins/adb/plugin.js index 6103aa69..c10c86eb 100644 --- a/src/core/plugins/adb/plugin.js +++ b/src/core/plugins/adb/plugin.js @@ -221,6 +221,24 @@ class AdbPlugin extends Plugin { }) .then(() => null); // ensure null is returned } + + /** + * adb:assert_prop action + * @returns {Promise} + */ + action__assert_prop({ prop, value: expectedValue }) { + return Promise.resolve() + .then(() => { + this.event.emit("user:write:under", `Asserting ${prop} property`); + }) + .then(() => this.adb.getprop(prop)) + .then(actualValue => { + if (actualValue !== expectedValue) + throw new Error( + `Assertion error: property ${prop} to be ${expectedValue} but got ${actualValue}` + ); + }); + } } module.exports = AdbPlugin; diff --git a/src/core/plugins/adb/plugin.spec.js b/src/core/plugins/adb/plugin.spec.js index 66c41d67..b8f7f602 100644 --- a/src/core/plugins/adb/plugin.spec.js +++ b/src/core/plugins/adb/plugin.spec.js @@ -169,4 +169,44 @@ describe("adb plugin", () => { }); }); }); + + describe("action__assert_prop()", () => { + it("should assert prop", () => { + jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("asdf"); + return adbPlugin + .action__assert_prop({ + prop: "somevar", + value: "asdf" + }) + .then(r => { + expect(r).not.toBeDefined(); + expect(adbPlugin.adb.getprop).toHaveBeenCalledWith("somevar"); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + "Asserting somevar property" + ); + adbPlugin.adb.getprop.mockRestore(); + }); + }); + it("should fail assertion", done => { + jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("wasd"); + adbPlugin + .action__assert_prop({ + prop: "somevar", + value: "asdf" + }) + .catch(e => { + expect(e.message).toEqual( + "Assertion error: property somevar to be asdf but got wasd" + ); + expect(adbPlugin.adb.getprop).toHaveBeenCalledWith("somevar"); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + "Asserting somevar property" + ); + adbPlugin.adb.getprop.mockRestore(); + done(); + }); + }); + }); }); diff --git a/src/core/plugins/fastboot/plugin.js b/src/core/plugins/fastboot/plugin.js index 88d9a39f..02abaa57 100644 --- a/src/core/plugins/fastboot/plugin.js +++ b/src/core/plugins/fastboot/plugin.js @@ -318,6 +318,27 @@ class FastbootPlugin extends Plugin { .then(() => this.fastboot.wait()) .then(() => null); // ensure null is returned } + + /** + * fastboot:wait action + * @returns {Promise} + */ + action__assert_var({ variable, value: expectedValue }) { + return Promise.resolve() + .then(() => { + this.event.emit( + "user:write:under", + `Asserting ${variable} bootloader variable` + ); + }) + .then(() => this.fastboot.getvar(variable)) + .then(actualValue => { + if (actualValue !== expectedValue) + throw new Error( + `Assertion error: expected bootloader variable ${variable} to be ${expectedValue} but got ${actualValue}` + ); + }); + } } module.exports = FastbootPlugin; diff --git a/src/core/plugins/fastboot/plugin.spec.js b/src/core/plugins/fastboot/plugin.spec.js index a43c8d5a..97497ff9 100644 --- a/src/core/plugins/fastboot/plugin.spec.js +++ b/src/core/plugins/fastboot/plugin.spec.js @@ -136,4 +136,47 @@ describe("fastboot plugin", () => { }); }); }); + describe("assert_var()", () => { + it("should assert var", () => { + jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("asdf"); + return fastbootPlugin + .action__assert_var({ + variable: "somevar", + value: "asdf" + }) + .then(r => { + expect(r).not.toBeDefined(); + expect(fastbootPlugin.fastboot.getvar).toHaveBeenCalledWith( + "somevar" + ); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + "Asserting somevar bootloader variable" + ); + fastbootPlugin.fastboot.getvar.mockRestore(); + }); + }); + it("should fail assertion", done => { + jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("wasd"); + fastbootPlugin + .action__assert_var({ + variable: "somevar", + value: "asdf" + }) + .catch(e => { + expect(e.message).toEqual( + "Assertion error: expected bootloader variable somevar to be asdf but got wasd" + ); + expect(fastbootPlugin.fastboot.getvar).toHaveBeenCalledWith( + "somevar" + ); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + "Asserting somevar bootloader variable" + ); + fastbootPlugin.fastboot.getvar.mockRestore(); + done(); + }); + }); + }); }); From 24883f3d2c96235c0046a8fa852ecee7d511c77d Mon Sep 17 00:00:00 2001 From: Johannah Sprinz Date: Thu, 24 Mar 2022 13:17:01 +0100 Subject: [PATCH 2/3] RegEx assertions Tweak adb:assert_prop and fastboot:assert_var to support regex as specified in https://github.com/ubports/installer-configs/pull/178 Example: ```yml - adb:assert_prop: prop: "ro.ubuntu.recovery" value: "true" - adb:assert_prop: prop: "ro.ubuntu.recovery" regex: pattern: "true" flags: "i" - fastboot:assert_var: variable: "ro.ubuntu.recovery" value: "true" - fastboot:assert_var: variable: "ro.ubuntu.recovery" regex: pattern: "true" flags: "i" ``` --- src/core/plugins/adb/plugin.js | 14 ++++- src/core/plugins/adb/plugin.spec.js | 70 ++++++++++++++++------- src/core/plugins/fastboot/plugin.js | 16 ++++-- src/core/plugins/fastboot/plugin.spec.js | 72 ++++++++++++++++-------- 4 files changed, 122 insertions(+), 50 deletions(-) diff --git a/src/core/plugins/adb/plugin.js b/src/core/plugins/adb/plugin.js index c10c86eb..4c60ebca 100644 --- a/src/core/plugins/adb/plugin.js +++ b/src/core/plugins/adb/plugin.js @@ -226,16 +226,24 @@ class AdbPlugin extends Plugin { * adb:assert_prop action * @returns {Promise} */ - action__assert_prop({ prop, value: expectedValue }) { + action__assert_prop({ prop, value: expectedValue, regex }) { return Promise.resolve() .then(() => { this.event.emit("user:write:under", `Asserting ${prop} property`); }) .then(() => this.adb.getprop(prop)) .then(actualValue => { - if (actualValue !== expectedValue) + if ( + !(regex + ? actualValue.match(new RegExp(regex.pattern, regex.flags)) + : actualValue === expectedValue) + ) throw new Error( - `Assertion error: property ${prop} to be ${expectedValue} but got ${actualValue}` + `Assertion error: expected property ${prop} to ${ + regex + ? `match /${regex.pattern}/${regex.flags || ""}` + : `be ${expectedValue}` + } but got ${actualValue}` ); }); } diff --git a/src/core/plugins/adb/plugin.spec.js b/src/core/plugins/adb/plugin.spec.js index b8f7f602..a7d64ded 100644 --- a/src/core/plugins/adb/plugin.spec.js +++ b/src/core/plugins/adb/plugin.spec.js @@ -171,42 +171,70 @@ describe("adb plugin", () => { }); describe("action__assert_prop()", () => { - it("should assert prop", () => { - jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("asdf"); - return adbPlugin - .action__assert_prop({ + [ + { + comment: "value", + arg: { prop: "somevar", value: "asdf" - }) - .then(r => { + } + }, + { + comment: "regex w/ flags", + arg: { + prop: "somevar", + regex: { + pattern: "a[s,d]*f", + flags: "i" + } + } + }, + { + comment: "regex w/o flags", + arg: { + prop: "somevar", + regex: { + pattern: "a[s,d]*f" + } + } + }, + { + comment: "string in regex", + arg: { + prop: "somevar", + regex: { + pattern: "asdf" + } + } + } + ].forEach(({ comment, arg }) => { + it(`should assert and pass prop from ${comment}`, () => { + jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("asdf"); + return adbPlugin.action__assert_prop(arg).then(r => { expect(r).not.toBeDefined(); - expect(adbPlugin.adb.getprop).toHaveBeenCalledWith("somevar"); + expect(adbPlugin.adb.getprop).toHaveBeenCalledWith(arg.prop); expect(mainEvent.emit).toHaveBeenCalledWith( "user:write:under", - "Asserting somevar property" + expect.stringMatching(/Asserting .* property/) ); adbPlugin.adb.getprop.mockRestore(); }); - }); - it("should fail assertion", done => { - jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("wasd"); - adbPlugin - .action__assert_prop({ - prop: "somevar", - value: "asdf" - }) - .catch(e => { - expect(e.message).toEqual( - "Assertion error: property somevar to be asdf but got wasd" + }); + it(`should assert and fail prop from ${comment}`, done => { + jest.spyOn(adbPlugin.adb, "getprop").mockResolvedValue("wasd"); + adbPlugin.action__assert_prop(arg).catch(e => { + expect(e.message).toMatch( + /Assertion error: expected property .* to (be|match) .* but got wasd/ ); - expect(adbPlugin.adb.getprop).toHaveBeenCalledWith("somevar"); + expect(adbPlugin.adb.getprop).toHaveBeenCalledWith(arg.prop); expect(mainEvent.emit).toHaveBeenCalledWith( "user:write:under", - "Asserting somevar property" + expect.stringMatching(/Asserting .* property/) ); adbPlugin.adb.getprop.mockRestore(); done(); }); + }); }); }); }); diff --git a/src/core/plugins/fastboot/plugin.js b/src/core/plugins/fastboot/plugin.js index 02abaa57..3263c023 100644 --- a/src/core/plugins/fastboot/plugin.js +++ b/src/core/plugins/fastboot/plugin.js @@ -320,10 +320,10 @@ class FastbootPlugin extends Plugin { } /** - * fastboot:wait action + * fastboot:assert_var action * @returns {Promise} */ - action__assert_var({ variable, value: expectedValue }) { + action__assert_var({ variable, value: expectedValue, regex }) { return Promise.resolve() .then(() => { this.event.emit( @@ -333,9 +333,17 @@ class FastbootPlugin extends Plugin { }) .then(() => this.fastboot.getvar(variable)) .then(actualValue => { - if (actualValue !== expectedValue) + if ( + !(regex + ? actualValue.match(new RegExp(regex.pattern, regex.flags)) + : actualValue === expectedValue) + ) throw new Error( - `Assertion error: expected bootloader variable ${variable} to be ${expectedValue} but got ${actualValue}` + `Assertion error: expected bootloader variable ${variable} to ${ + regex + ? `match /${regex.pattern}/${regex.flags || ""}` + : `be ${expectedValue}` + } but got ${actualValue}` ); }); } diff --git a/src/core/plugins/fastboot/plugin.spec.js b/src/core/plugins/fastboot/plugin.spec.js index 97497ff9..8f93a85d 100644 --- a/src/core/plugins/fastboot/plugin.spec.js +++ b/src/core/plugins/fastboot/plugin.spec.js @@ -136,47 +136,75 @@ describe("fastboot plugin", () => { }); }); }); - describe("assert_var()", () => { - it("should assert var", () => { - jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("asdf"); - return fastbootPlugin - .action__assert_var({ + describe("action__assert_var()", () => { + [ + { + comment: "value", + arg: { variable: "somevar", value: "asdf" - }) - .then(r => { + } + }, + { + comment: "regex w/ flags", + arg: { + variable: "somevar", + regex: { + pattern: "a[s,d]*f", + flags: "i" + } + } + }, + { + comment: "regex w/o flags", + arg: { + variable: "somevar", + regex: { + pattern: "a[s,d]*f" + } + } + }, + { + comment: "string in regex", + arg: { + variable: "somevar", + regex: { + pattern: "asdf" + } + } + } + ].forEach(({ comment, arg }) => { + it(`should assert and pass variable from ${comment}`, () => { + jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("asdf"); + return fastbootPlugin.action__assert_var(arg).then(r => { expect(r).not.toBeDefined(); expect(fastbootPlugin.fastboot.getvar).toHaveBeenCalledWith( - "somevar" + arg.variable ); expect(mainEvent.emit).toHaveBeenCalledWith( "user:write:under", - "Asserting somevar bootloader variable" + expect.stringMatching(/Asserting .* bootloader variable/) ); fastbootPlugin.fastboot.getvar.mockRestore(); }); - }); - it("should fail assertion", done => { - jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("wasd"); - fastbootPlugin - .action__assert_var({ - variable: "somevar", - value: "asdf" - }) - .catch(e => { - expect(e.message).toEqual( - "Assertion error: expected bootloader variable somevar to be asdf but got wasd" + }); + it(`should assert and fail variable from ${comment}`, done => { + jest.spyOn(fastbootPlugin.fastboot, "getvar").mockResolvedValue("wasd"); + fastbootPlugin.action__assert_var(arg).catch(e => { + expect(e.message).toMatch( + /Assertion error: expected bootloader variable .* to (be|match) .* but got wasd/ ); expect(fastbootPlugin.fastboot.getvar).toHaveBeenCalledWith( - "somevar" + arg.variable ); expect(mainEvent.emit).toHaveBeenCalledWith( "user:write:under", - "Asserting somevar bootloader variable" + expect.stringMatching(/Asserting .* bootloader variable/) ); fastbootPlugin.fastboot.getvar.mockRestore(); done(); }); + }); }); }); }); From c01c54a3a56b32e584c0b2e7fd20d69e273473cf Mon Sep 17 00:00:00 2001 From: Johannah Sprinz Date: Thu, 24 Mar 2022 13:31:53 +0100 Subject: [PATCH 3/3] Implement verify_recovery for systemimage:install Option as specified in https://github.com/ubports/installer-configs/pull/178 Example: ```yml - systemimage:install: verify_recovery: true ``` --- src/core/plugins/systemimage/plugin.js | 12 +++++++++++- src/core/plugins/systemimage/plugin.spec.js | 12 ++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/core/plugins/systemimage/plugin.js b/src/core/plugins/systemimage/plugin.js index 5398c787..ae8c9f8e 100644 --- a/src/core/plugins/systemimage/plugin.js +++ b/src/core/plugins/systemimage/plugin.js @@ -30,7 +30,7 @@ class SystemimagePlugin extends Plugin { * systemimage:install action * @returns {Promise>} */ - action__install() { + action__install({ verify_recovery } = {}) { return api .getImages( this.props.settings.channel, @@ -56,6 +56,16 @@ class SystemimagePlugin extends Plugin { { "adb:wait": null }, + ...(verify_recovery + ? [ + { + "adb:assert_prop": { + prop: "ro.ubuntu.recovery", + value: "true" + } + } + ] + : []), { "adb:preparesystemimage": null }, diff --git a/src/core/plugins/systemimage/plugin.spec.js b/src/core/plugins/systemimage/plugin.spec.js index 300edeb3..16a7756e 100644 --- a/src/core/plugins/systemimage/plugin.spec.js +++ b/src/core/plugins/systemimage/plugin.spec.js @@ -19,8 +19,8 @@ beforeEach(() => jest.clearAllMocks()); describe("systemimage plugin", () => { describe("actions", () => { - describe("download", () => { - it("should create download steps", () => + describe("install", () => { + it("should create install actions", () => systemimage.action__install().then(r => { expect(r).toHaveLength(1); expect(r[0].actions).toHaveLength(5); @@ -49,6 +49,14 @@ describe("systemimage plugin", () => { } }); })); + it("should verify recovery", () => + systemimage.action__install({ verify_recovery: true }).then(r => { + expect(r).toHaveLength(1); + expect(r[0].actions).toHaveLength(6); + expect(r[0].actions).toContainEqual({ + "adb:assert_prop": { prop: "ro.ubuntu.recovery", value: "true" } + }); + })); }); }); describe("remote_values", () => {