diff --git a/lib/git-shell-out-strategy.js b/lib/git-shell-out-strategy.js index d74388bc2c..60a0ef543c 100644 --- a/lib/git-shell-out-strategy.js +++ b/lib/git-shell-out-strategy.js @@ -38,8 +38,14 @@ export default class GitShellOutStrategy { options.stdinEncoding = 'utf8' } + if (process.env.PRINT_GIT_TIMES) { + console.time(`git:${formattedArgs}`) + } return GitProcess.exec(args, this.workingDir, options) .then(({stdout, stderr, exitCode}) => { + if (process.env.PRINT_GIT_TIMES) { + console.timeEnd(`git:${formattedArgs}`) + } if (exitCode) { const err = new Error(`${formattedArgs} exited with code ${exitCode}\nstdout: ${stdout}\nstderr: ${stderr}`) err.code = exitCode diff --git a/lib/models/repository.js b/lib/models/repository.js index 3fa5bb511a..980be35b5e 100644 --- a/lib/models/repository.js +++ b/lib/models/repository.js @@ -81,7 +81,7 @@ export default class Repository { } async refresh () { - if (global.PRINT_GIT_TIMES) console.time('refresh') + if (process.env.PRINT_GIT_TIMES) console.time('refresh') this.stagedChangesPromise = this.fetchStagedChanges() this.stagedChangesSinceParentCommitPromise = this.fetchStagedChangesSinceParentCommit() this.unstagedChangesPromise = this.fetchUnstagedChanges() @@ -92,7 +92,7 @@ export default class Repository { this.unstagedChangesPromise, this.mergeConflictsPromise ]) - if (global.PRINT_GIT_TIMES) console.timeEnd('refresh') + if (process.env.PRINT_GIT_TIMES) console.timeEnd('refresh') this.didUpdate() } diff --git a/test/controllers/file-patch-controller.test.js b/test/controllers/file-patch-controller.test.js index 1de0be1f71..2314d3427a 100644 --- a/test/controllers/file-patch-controller.test.js +++ b/test/controllers/file-patch-controller.test.js @@ -92,12 +92,12 @@ describe('FilePatchController', () => { 'this is a new line', 'this is another new line' ) - assert.equal(await repository.readFileFromIndex('sample.js'), expectedStagedLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedStagedLines.join('\n')) const [stagedFilePatch] = await repository.getStagedChanges() await controller.update({filePatch: stagedFilePatch, repository, stagingStatus: 'staged', registerHunkView}) await hunkViewsByHunk.get(stagedFilePatch.getHunks()[0]).props.didClickStageButton() - assert.equal(await repository.readFileFromIndex('sample.js'), originalLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), originalLines.join('\n')) }) it('stages and unstages individual lines when the stage button is clicked on a hunk with selected lines', async () => { @@ -134,7 +134,7 @@ describe('FilePatchController', () => { 'this is a modified line', 'this is a new line' ) - assert.equal(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) // stage remaining lines in hunk await hunkView.props.didClickStageButton() @@ -144,7 +144,7 @@ describe('FilePatchController', () => { 'this is a new line', 'this is another new line' ) - assert.equal(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) // unstage a subset of lines from the first hunk const [stagedFilePatch] = await repository.getStagedChanges() @@ -163,12 +163,12 @@ describe('FilePatchController', () => { 'this is a new line', 'this is another new line' ) - assert.equal(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines.join('\n')) // unstage the rest of the hunk await view.togglePatchSelectionMode() await hunkView.props.didClickStageButton() - assert.equal(await repository.readFileFromIndex('sample.js'), originalLines.join('\n')) + assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), originalLines.join('\n')) }) }) }) diff --git a/test/git-shell-out-strategy.test.js b/test/git-shell-out-strategy.test.js index b917f5dea0..576edb8ec6 100644 --- a/test/git-shell-out-strategy.test.js +++ b/test/git-shell-out-strategy.test.js @@ -109,8 +109,8 @@ describe('Git commands', () => { fs.writeFileSync(path.join(folderPath, 'f.txt'), 'qux', 'utf8') assert.deepEqual(await git.getUntrackedFiles(), [ 'd.txt', - path.join('folder', 'subfolder', 'e.txt'), - path.join('folder', 'subfolder', 'f.txt') + 'folder/subfolder/e.txt', + 'folder/subfolder/f.txt' ]) }) @@ -284,6 +284,9 @@ describe('Git commands', () => { await git.merge('origin/branch') } catch (e) { // expected + if (!e.message.match(/CONFLICT/)) { + throw new Error(`merge failed for wrong reason: ${e.message}`) + } } const statusesByPath = await git.getMergeConflictFileStatus() diff --git a/test/helpers.js b/test/helpers.js index 3c3e1af273..b9650cd86f 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -8,13 +8,34 @@ import GitShellOutStrategy from '../lib/git-shell-out-strategy' import Repository from '../lib/models/repository' -export async function cloneRepository (repoName = 'three-files') { +assert.autocrlfEqual = (actual, expected, ...args) => { + actual = actual.replace(/\r\n/g, '\n') + expected = expected.replace(/\r\n/g, '\n') + return assert.equal(actual, expected, ...args) +} + +// cloning a repo into a folder and then copying it +// for each subsequent request to clone makes cloning +// 2-3x faster on macOS and 5-10x faster on Windows +const cachedClonedRepos = {} +function copyCachedRepo(repoName) { const workingDirPath = temp.mkdirSync('git-fixture-') - const git = new GitShellOutStrategy(workingDirPath) - await git.clone(path.join(__dirname, 'fixtures', `repo-${repoName}`, 'dot-git')) + fs.copySync(cachedClonedRepos[repoName], workingDirPath) return fs.realpathSync(workingDirPath) } +export async function cloneRepository (repoName = 'three-files') { + if (!cachedClonedRepos[repoName]) { + const cachedPath = temp.mkdirSync('git-fixture-cache-') + const git = new GitShellOutStrategy(cachedPath) + await git.clone(path.join(__dirname, 'fixtures', `repo-${repoName}`, 'dot-git')) + await git.exec(['config', '--local', 'core.autocrlf', 'false']) + await git.exec(['checkout', '--', '.']) // discard \r in working directory + cachedClonedRepos[repoName] = cachedPath + } + return copyCachedRepo(repoName) +} + export async function setUpLocalAndRemoteRepositories (repoName = 'multiple-commits', options = {}) { if (typeof repoName === 'object') { options = repoName diff --git a/test/models/file-system-change-observer.test.js b/test/models/file-system-change-observer.test.js index 9870d07675..bea966104a 100644 --- a/test/models/file-system-change-observer.test.js +++ b/test/models/file-system-change-observer.test.js @@ -88,12 +88,12 @@ describe('FileSystemChangeObserver', async () => { fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n') await repository.git.exec(['add', 'a.txt']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) changeSpy.reset() await repository.git.exec(['reset', 'a.txt']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) }) it('emits an event when a branch is checked out', async () => { @@ -107,7 +107,7 @@ describe('FileSystemChangeObserver', async () => { await repository.git.exec(['checkout', '-b', 'new-branch']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) }) it('emits an event when commits are pushed', async () => { @@ -125,7 +125,7 @@ describe('FileSystemChangeObserver', async () => { changeSpy.reset() await repository.git.exec(['push', 'origin', 'master']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) }) it('emits an event when a new tracking branch is added after pushing', async () => { @@ -143,7 +143,7 @@ describe('FileSystemChangeObserver', async () => { changeSpy.reset() await repository.git.exec(['push', '--set-upstream', 'origin', 'new-branch']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) }) it('emits an event when commits have been fetched', async () => { @@ -157,7 +157,7 @@ describe('FileSystemChangeObserver', async () => { await repository.git.exec(['fetch', 'origin', 'master']) await changeObserver.lastFileChangePromise - assert.isTrue(changeSpy.calledOnce) + assert.isTrue(changeSpy.called) }) function timeout(ms) { diff --git a/test/models/repository.test.js b/test/models/repository.test.js index 8ce4319375..f86dd68079 100644 --- a/test/models/repository.test.js +++ b/test/models/repository.test.js @@ -499,6 +499,9 @@ describe('Repository', function () { await repo.git.merge('origin/branch') } catch (e) { // expected + if (!e.message.match(/CONFLICT/)) { + throw new Error(`merge failed for wrong reason: ${e.message}`) + } } await repo.refresh() diff --git a/test/models/workspace-change-observer.test.js b/test/models/workspace-change-observer.test.js index 92d4fc9450..0a7a9a7dd6 100644 --- a/test/models/workspace-change-observer.test.js +++ b/test/models/workspace-change-observer.test.js @@ -9,92 +9,95 @@ import {cloneRepository, buildRepository} from '../helpers' import WorkspaceChangeObserver from '../../lib/models/workspace-change-observer' -describe('WorkspaceChangeObserver', () => { - let atomEnv, workspace - - beforeEach(async () => { - atomEnv = global.buildAtomEnvironment() - workspace = atomEnv.workspace - }) - - afterEach(() => { - atomEnv.destroy() - }) - - it('emits a change event when the window is focused', async () => { - const changeSpy = sinon.spy() - const changeObserver = new WorkspaceChangeObserver(window, workspace) - changeObserver.onDidChange(changeSpy) - - window.dispatchEvent(new FocusEvent('focus')) - assert.isFalse(changeSpy.called) - - await changeObserver.start() - window.dispatchEvent(new FocusEvent('focus')) - assert.isFalse(changeSpy.called) - - const workdirPath = await cloneRepository('three-files') - const repository = await buildRepository(workdirPath) - await changeObserver.setActiveRepository(repository) - window.dispatchEvent(new FocusEvent('focus')) - assert.isTrue(changeSpy.calledOnce) - }) - - it('emits a change event when a buffer belonging to the active directory changes', async () => { - const changeSpy = sinon.spy() - const changeObserver = new WorkspaceChangeObserver(window, workspace) - changeObserver.onDidChange(changeSpy) - await changeObserver.start() - const workdirPath1 = await cloneRepository('three-files') - const repository1 = await buildRepository(workdirPath1) - const workdirPath2 = await cloneRepository('three-files') - const repository2 = await buildRepository(workdirPath2) - - const editor1 = await workspace.open(path.join(workdirPath1, 'a.txt')) - const editor2 = await workspace.open(path.join(workdirPath2, 'a.txt')) - await changeObserver.setActiveRepository(repository2) - editor1.setText('change') - editor1.save() - assert.isFalse(changeSpy.called) - editor2.setText('change') - editor2.save() - assert.isTrue(changeSpy.calledOnce) - - changeSpy.reset() - await changeObserver.setActiveRepository(repository1) - editor2.setText('change 1') - editor2.save() - assert.isFalse(changeSpy.called) - editor1.setText('change 1') - editor1.save() - assert.isTrue(changeSpy.calledOnce) - - changeSpy.reset() - repository1.refresh() - const [unstagedChange] = await repository1.getUnstagedChanges() - await repository1.applyPatchToIndex(unstagedChange) - await changeObserver.lastIndexChangePromise - assert.isTrue(changeSpy.called) - - changeSpy.reset() - editor1.getBuffer().reload() - assert.isTrue(changeSpy.calledOnce) - - changeSpy.reset() - editor1.destroy() - assert.isTrue(changeSpy.calledOnce) - - changeSpy.reset() - await changeObserver.setActiveRepository(null) - editor2.setText('change 2') - editor2.save() - assert.isFalse(changeSpy.called) - - changeSpy.reset() - await changeObserver.setActiveRepository(repository2) - await changeObserver.stop() - editor2.setText('change 2') - editor2.save() - assert.isFalse(changeSpy.called) +// This class only works on Linux +if (process.platform === 'linux') { + describe('WorkspaceChangeObserver', () => { + let atomEnv, workspace + + beforeEach(async () => { + atomEnv = global.buildAtomEnvironment() + workspace = atomEnv.workspace + }) + + afterEach(() => { + atomEnv.destroy() + }) + + it('emits a change event when the window is focused', async () => { + const changeSpy = sinon.spy() + const changeObserver = new WorkspaceChangeObserver(window, workspace) + changeObserver.onDidChange(changeSpy) + + window.dispatchEvent(new FocusEvent('focus')) + assert.isFalse(changeSpy.called) + + await changeObserver.start() + window.dispatchEvent(new FocusEvent('focus')) + assert.isFalse(changeSpy.called) + + const workdirPath = await cloneRepository('three-files') + const repository = await buildRepository(workdirPath) + await changeObserver.setActiveRepository(repository) + window.dispatchEvent(new FocusEvent('focus')) + assert.isTrue(changeSpy.calledOnce) + }) + + it('emits a change event when a buffer belonging to the active directory changes', async () => { + const changeSpy = sinon.spy() + const changeObserver = new WorkspaceChangeObserver(window, workspace) + changeObserver.onDidChange(changeSpy) + await changeObserver.start() + const workdirPath1 = await cloneRepository('three-files') + const repository1 = await buildRepository(workdirPath1) + const workdirPath2 = await cloneRepository('three-files') + const repository2 = await buildRepository(workdirPath2) + + const editor1 = await workspace.open(path.join(workdirPath1, 'a.txt')) + const editor2 = await workspace.open(path.join(workdirPath2, 'a.txt')) + await changeObserver.setActiveRepository(repository2) + editor1.setText('change') + editor1.save() + assert.isFalse(changeSpy.called) + editor2.setText('change') + editor2.save() + assert.isTrue(changeSpy.calledOnce) + + changeSpy.reset() + await changeObserver.setActiveRepository(repository1) + editor2.setText('change 1') + editor2.save() + assert.isFalse(changeSpy.called) + editor1.setText('change 1') + editor1.save() + assert.isTrue(changeSpy.calledOnce) + + changeSpy.reset() + repository1.refresh() + const [unstagedChange] = await repository1.getUnstagedChanges() + await repository1.applyPatchToIndex(unstagedChange) + await changeObserver.lastIndexChangePromise + assert.isTrue(changeSpy.called) + + changeSpy.reset() + editor1.getBuffer().reload() + assert.isTrue(changeSpy.calledOnce) + + changeSpy.reset() + editor1.destroy() + assert.isTrue(changeSpy.calledOnce) + + changeSpy.reset() + await changeObserver.setActiveRepository(null) + editor2.setText('change 2') + editor2.save() + assert.isFalse(changeSpy.called) + + changeSpy.reset() + await changeObserver.setActiveRepository(repository2) + await changeObserver.stop() + editor2.setText('change 2') + editor2.save() + assert.isFalse(changeSpy.called) + }) }) -}) +} diff --git a/test/runner.js b/test/runner.js index 8da6efafc5..e64e91c7a9 100644 --- a/test/runner.js +++ b/test/runner.js @@ -9,4 +9,4 @@ global.assert = chai.assert module.exports = createRunner({ overrideTestPaths: [/spec$/, /test/] -}, mocha => mocha.timeout(5000)) +}, mocha => mocha.timeout(20000))