diff --git a/spec/main-process/file-recovery-service.spec.js b/spec/main-process/file-recovery-service.spec.js index 58857fa1d..01d625cfc 100644 --- a/spec/main-process/file-recovery-service.spec.js +++ b/spec/main-process/file-recovery-service.spec.js @@ -65,42 +65,34 @@ describe("FileRecoveryService", () => { assert.equal(fs.readFileSync(filePath, 'utf8'), "some content") }) - describe("when many windows attempt to save the same file", () => { - it("recovers the file when the window that initiated the save crashes", () => { - const mockWindow = {} - const anotherMockWindow = {} - const filePath = temp.path() + it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", () => { + const mockWindow = {} + const anotherMockWindow = {} + const filePath = temp.path() - fs.writeFileSync(filePath, "window 1") - recoveryService.willSavePath(mockWindow, filePath) - fs.writeFileSync(filePath, "window 2") - recoveryService.willSavePath(anotherMockWindow, filePath) - assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) + fs.writeFileSync(filePath, "A") + recoveryService.willSavePath(mockWindow, filePath) + fs.writeFileSync(filePath, "B") + recoveryService.willSavePath(anotherMockWindow, filePath) + assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "changed") + fs.writeFileSync(filePath, "C") - recoveryService.didCrashWindow(mockWindow) - assert.equal(fs.readFileSync(filePath, 'utf8'), "window 1") - assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - }) + recoveryService.didCrashWindow(mockWindow) + assert.equal(fs.readFileSync(filePath, 'utf8'), "A") + assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - it("recovers the file when a window that did not initiate the save crashes", () => { - const mockWindow = {} - const anotherMockWindow = {} - const filePath = temp.path() + fs.writeFileSync(filePath, "D") + recoveryService.willSavePath(mockWindow, filePath) + fs.writeFileSync(filePath, "E") + recoveryService.willSavePath(anotherMockWindow, filePath) + assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "window 1") - recoveryService.willSavePath(mockWindow, filePath) - fs.writeFileSync(filePath, "window 2") - recoveryService.willSavePath(anotherMockWindow, filePath) - assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) + fs.writeFileSync(filePath, "F") - fs.writeFileSync(filePath, "changed") - - recoveryService.didCrashWindow(anotherMockWindow) - assert.equal(fs.readFileSync(filePath, 'utf8'), "window 1") - assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - }) + recoveryService.didCrashWindow(anotherMockWindow) + assert.equal(fs.readFileSync(filePath, 'utf8'), "D") + assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) }) it("emits a warning when a file can't be recovered", sinon.test(function () { diff --git a/src/main-process/file-recovery-service.js b/src/main-process/file-recovery-service.js index c7e4d6c3b..235fd05f4 100644 --- a/src/main-process/file-recovery-service.js +++ b/src/main-process/file-recovery-service.js @@ -10,6 +10,7 @@ export default class FileRecoveryService { this.recoveryDirectory = recoveryDirectory this.recoveryFilesByFilePath = new Map() this.recoveryFilesByWindow = new WeakMap() + this.windowsByRecoveryFile = new Map() } willSavePath (window, path) { @@ -33,7 +34,11 @@ export default class FileRecoveryService { if (!this.recoveryFilesByWindow.has(window)) { this.recoveryFilesByWindow.set(window, new Set()) } + if (!this.windowsByRecoveryFile.has(recoveryFile)) { + this.windowsByRecoveryFile.set(recoveryFile, new Set()) + } this.recoveryFilesByWindow.get(window).add(recoveryFile) + this.windowsByRecoveryFile.get(recoveryFile).add(window) } didSavePath (window, path) { @@ -46,6 +51,7 @@ export default class FileRecoveryService { } if (recoveryFile.isReleased()) this.recoveryFilesByFilePath.delete(path) this.recoveryFilesByWindow.get(window).delete(recoveryFile) + this.windowsByRecoveryFile.get(recoveryFile).delete(window) } } @@ -58,19 +64,26 @@ export default class FileRecoveryService { } catch (error) { const message = 'A file that Atom was saving could be corrupted' const detail = - `There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + + `Error ${error.code}. There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + `Atom couldn't recover it automatically, but a recovery file has been saved at: "${recoveryFile.recoveryPath}".` console.log(detail) dialog.showMessageBox(window.browserWindow, {type: 'info', buttons: ['OK'], message, detail}) } finally { + for (let window of this.windowsByRecoveryFile.get(recoveryFile)) { + this.recoveryFilesByWindow.get(window).delete(recoveryFile) + } + this.windowsByRecoveryFile.delete(recoveryFile) this.recoveryFilesByFilePath.delete(recoveryFile.originalPath) } } - - this.recoveryFilesByWindow.delete(window) } didCloseWindow (window) { + if (!this.recoveryFilesByWindow.has(window)) return + + for (let recoveryFile of this.recoveryFilesByWindow.get(window)) { + this.windowsByRecoveryFile.get(recoveryFile).delete(window) + } this.recoveryFilesByWindow.delete(window) } } @@ -96,7 +109,6 @@ class RecoveryFile { recoverSync () { fs.writeFileSync(this.originalPath, fs.readFileSync(this.recoveryPath)) this.removeSync() - this.refCount = 0 } removeSync () {