diff --git a/src/core/plugins/adb/plugin.js b/src/core/plugins/adb/plugin.js index 6103aa69..4c60ebca 100644 --- a/src/core/plugins/adb/plugin.js +++ b/src/core/plugins/adb/plugin.js @@ -221,6 +221,32 @@ class AdbPlugin extends Plugin { }) .then(() => null); // ensure null is returned } + + /** + * adb:assert_prop action + * @returns {Promise} + */ + 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 ( + !(regex + ? actualValue.match(new RegExp(regex.pattern, regex.flags)) + : actualValue === expectedValue) + ) + throw new Error( + `Assertion error: expected property ${prop} to ${ + regex + ? `match /${regex.pattern}/${regex.flags || ""}` + : `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..a7d64ded 100644 --- a/src/core/plugins/adb/plugin.spec.js +++ b/src/core/plugins/adb/plugin.spec.js @@ -169,4 +169,72 @@ describe("adb plugin", () => { }); }); }); + + describe("action__assert_prop()", () => { + [ + { + comment: "value", + arg: { + prop: "somevar", + value: "asdf" + } + }, + { + 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(arg.prop); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + expect.stringMatching(/Asserting .* property/) + ); + adbPlugin.adb.getprop.mockRestore(); + }); + }); + 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(arg.prop); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + 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 88d9a39f..3263c023 100644 --- a/src/core/plugins/fastboot/plugin.js +++ b/src/core/plugins/fastboot/plugin.js @@ -318,6 +318,35 @@ class FastbootPlugin extends Plugin { .then(() => this.fastboot.wait()) .then(() => null); // ensure null is returned } + + /** + * fastboot:assert_var action + * @returns {Promise} + */ + action__assert_var({ variable, value: expectedValue, regex }) { + return Promise.resolve() + .then(() => { + this.event.emit( + "user:write:under", + `Asserting ${variable} bootloader variable` + ); + }) + .then(() => this.fastboot.getvar(variable)) + .then(actualValue => { + if ( + !(regex + ? actualValue.match(new RegExp(regex.pattern, regex.flags)) + : actualValue === expectedValue) + ) + throw new Error( + `Assertion error: expected bootloader variable ${variable} to ${ + regex + ? `match /${regex.pattern}/${regex.flags || ""}` + : `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..8f93a85d 100644 --- a/src/core/plugins/fastboot/plugin.spec.js +++ b/src/core/plugins/fastboot/plugin.spec.js @@ -136,4 +136,75 @@ describe("fastboot plugin", () => { }); }); }); + describe("action__assert_var()", () => { + [ + { + comment: "value", + arg: { + variable: "somevar", + value: "asdf" + } + }, + { + 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( + arg.variable + ); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + expect.stringMatching(/Asserting .* bootloader variable/) + ); + fastbootPlugin.fastboot.getvar.mockRestore(); + }); + }); + 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( + arg.variable + ); + expect(mainEvent.emit).toHaveBeenCalledWith( + "user:write:under", + expect.stringMatching(/Asserting .* bootloader variable/) + ); + fastbootPlugin.fastboot.getvar.mockRestore(); + done(); + }); + }); + }); + }); }); 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", () => {