diff --git a/RestartManager.js b/RestartManager.js index 1f0d6f3..bdaddf9 100644 --- a/RestartManager.js +++ b/RestartManager.js @@ -3,9 +3,12 @@ const NativeCodePush = require("react-native").NativeModules.CodePush; const CodePush = require("./CodePush"); const RestartManager = (() => { - let _inProgress = false; + let _inProgressPromise = null; + let _inProgressOnUpdateOnly = false; + let _allowed = true; let _restartPending = false; + let _restartPendingOnUpdateOnly = false; function allow() { log("Re-allowing restarts"); @@ -13,7 +16,7 @@ const RestartManager = (() => { if (_restartPending) { log("Executing pending restart"); - restartApp(true); + restartApp(_restartPendingOnUpdateOnly); } } @@ -27,18 +30,42 @@ const RestartManager = (() => { } function restartApp(onlyIfUpdateIsPending = false) { - if (_allowed) { - if (_inProgress) { - log("A restart request is already in progress or queued"); + (async function(onlyIfUpdateIsPending) { + var didRestartSucceed = false; + + if (_restartPending) { + _restartPendingOnUpdateOnly = _restartPendingOnUpdateOnly && onlyIfUpdateIsPending; + log("Restart request queued until restarts are re-allowed"); return; } - // The restart won't execute if `onlyIfUpdateIsPending === true` and there is no pending update. - _inProgress = !onlyIfUpdateIsPending || !!(NativeCodePush.getUpdateMetadata(CodePush.UpdateState.PENDING)); - NativeCodePush.restartApp(onlyIfUpdateIsPending); + + if (!!_inProgressPromise) { + didRestartSucceed = await _inProgressPromise; + if (didRestartSucceed) { + log("A restart is already in progress."); + return; + } + } + + _inProgressPromise = new Promise(async function(resolve, reject) { + resolve(await restartAppInternal(onlyIfUpdateIsPending)); + }); + _inProgressOnUpdateOnly = onlyIfUpdateIsPending; + + didRestartSucceed = await _inProgressPromise; + if (!didRestartSucceed) _inProgressPromise = null; + })(onlyIfUpdateIsPending); + }; + + async function restartAppInternal(onlyIfUpdateIsPending = false) { + if (_allowed) { + var didRestartSucceed = await NativeCodePush.restartApp(onlyIfUpdateIsPending); log("Restarting app"); + return didRestartSucceed; } else { log("Restart request queued until restarts are re-allowed"); _restartPending = true; + _restartPendingOnUpdateOnly = onlyIfUpdateIsPending; return true; } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java index bd7585e..911ceb7 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java @@ -738,12 +738,14 @@ public class CodePush implements ReactPackage { } @ReactMethod - public void restartApp(boolean onlyIfUpdateIsPending) { + public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) { // If this is an unconditional restart request, or there // is current pending update, then reload the app. if (!onlyIfUpdateIsPending || CodePush.this.isPendingUpdate(null)) { loadBundle(); + promise.resolve(true); } + promise.resolve(false); } @ReactMethod diff --git a/code-push-plugin-testing-framework/script/testBuilder.js b/code-push-plugin-testing-framework/script/testBuilder.js index 5eb311b..c61d7a4 100644 --- a/code-push-plugin-testing-framework/script/testBuilder.js +++ b/code-push-plugin-testing-framework/script/testBuilder.js @@ -64,7 +64,7 @@ function itInternal(func, expectation, isCoreTest, assertion) { if ((!TestConfig.onlyRunCoreTests || isCoreTest)) { // Create a wrapper around the assertion to set the timeout on the test to 10 minutes. var assertionWithTimeout = function (done) { - this.timeout(10 * 60 * 1000); + this.timeout(20 * 60 * 1000); assertion(done); }; return it(expectation, assertionWithTimeout); diff --git a/code-push-plugin-testing-framework/script/testUtil.js b/code-push-plugin-testing-framework/script/testUtil.js index afe255a..ede3f11 100644 --- a/code-push-plugin-testing-framework/script/testUtil.js +++ b/code-push-plugin-testing-framework/script/testUtil.js @@ -48,7 +48,7 @@ var TestUtil = (function () { if (options.maxBuffer === undefined) options.maxBuffer = 1024 * 500; if (options.timeout === undefined) - options.timeout = 10 * 60 * 1000; + options.timeout = 20 * 60 * 1000; if (!options.noLogCommand) console.log("Running command: " + command); var execProcess = child_process.exec(command, options, function (error, stdout, stderr) { diff --git a/ios/CodePush/CodePush.m b/ios/CodePush/CodePush.m index c703092..6e2299e 100644 --- a/ios/CodePush/CodePush.m +++ b/ios/CodePush/CodePush.m @@ -740,13 +740,17 @@ RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve /* * This method is the native side of the CodePush.restartApp() method. */ -RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending) +RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending + resolve:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { // If this is an unconditional restart request, or there // is current pending update, then reload the app. if (!onlyIfUpdateIsPending || [self isPendingUpdate:nil]) { [self loadBundle]; + resolve(@(YES)); } + resolve(@(NO)); } #pragma mark - JavaScript-exported module methods (Private) diff --git a/test/template/scenarios/scenarioRestart2x.js b/test/template/scenarios/scenarioRestart2x.js index 819ec3b..11e775b 100644 --- a/test/template/scenarios/scenarioRestart2x.js +++ b/test/template/scenarios/scenarioRestart2x.js @@ -1,3 +1,4 @@ +var CodePushWrapper = require("../codePushWrapper.js"); import CodePush from "react-native-code-push"; module.exports = { diff --git a/test/test.ts b/test/test.ts index a0c6472..9399266 100644 --- a/test/test.ts +++ b/test/test.ts @@ -990,28 +990,28 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl TestBuilder.describe("#codePush.restartApplication.2x", () => { - // TestBuilder.it("blocks when a restart is in progress and doesn't crash if there is a pending package", false, - // (done: MochaDone) => { - // ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) }; - // setupTestRunScenario(projectManager, targetPlatform, ScenarioInstallRestart2x) - // .then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Good Update")) - // .then((updatePath: string) => { - // ServerUtil.updatePackagePath = updatePath; - // projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform); - // return ServerUtil.expectTestMessages([ - // ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE, - // ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED, - // ServerUtil.TestMessage.UPDATE_INSTALLED, - // ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]); - // }) - // .done(() => { done(); }, (e) => { done(e); }); - // }); + TestBuilder.it("blocks when a restart is in progress and doesn't crash if there is a pending package", false, + (done: MochaDone) => { + ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) }; + setupTestRunScenario(projectManager, targetPlatform, ScenarioInstallRestart2x) + .then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1")) + .then((updatePath: string) => { + ServerUtil.updatePackagePath = updatePath; + projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform); + return ServerUtil.expectTestMessages([ + ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE, + ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED, + ServerUtil.TestMessage.UPDATE_INSTALLED, + ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]); + }) + .done(() => { done(); }, (e) => { done(e); }); + }); TestBuilder.it("doesn't block when the restart is ignored", false, (done: MochaDone) => { ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) }; setupTestRunScenario(projectManager, targetPlatform, ScenarioRestart2x) - .then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Good Update")) + .then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1")) .then((updatePath: string) => { ServerUtil.updatePackagePath = updatePath; projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform);