path-watcher-spec.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /** @babel */
  2. import temp from 'temp';
  3. import fs from 'fs-plus';
  4. import path from 'path';
  5. import { promisify } from 'util';
  6. import { CompositeDisposable } from 'event-kit';
  7. import { watchPath, stopAllWatchers } from '../src/path-watcher';
  8. temp.track();
  9. const writeFile = promisify(fs.writeFile);
  10. const mkdir = promisify(fs.mkdir);
  11. const appendFile = promisify(fs.appendFile);
  12. const realpath = promisify(fs.realpath);
  13. const tempMkdir = promisify(temp.mkdir);
  14. describe('watchPath', function() {
  15. let subs;
  16. beforeEach(function() {
  17. subs = new CompositeDisposable();
  18. });
  19. afterEach(async function() {
  20. subs.dispose();
  21. await stopAllWatchers();
  22. });
  23. function waitForChanges(watcher, ...fileNames) {
  24. const waiting = new Set(fileNames);
  25. let fired = false;
  26. const relevantEvents = [];
  27. return new Promise(resolve => {
  28. const sub = watcher.onDidChange(events => {
  29. for (const event of events) {
  30. if (waiting.delete(event.path)) {
  31. relevantEvents.push(event);
  32. }
  33. }
  34. if (!fired && waiting.size === 0) {
  35. fired = true;
  36. resolve(relevantEvents);
  37. sub.dispose();
  38. }
  39. });
  40. });
  41. }
  42. describe('watchPath()', function() {
  43. it('resolves the returned promise when the watcher begins listening', async function() {
  44. const rootDir = await tempMkdir('atom-fsmanager-test-');
  45. const watcher = await watchPath(rootDir, {}, () => {});
  46. expect(watcher.constructor.name).toBe('PathWatcher');
  47. });
  48. it('reuses an existing native watcher and resolves getStartPromise immediately if attached to a running watcher', async function() {
  49. const rootDir = await tempMkdir('atom-fsmanager-test-');
  50. const watcher0 = await watchPath(rootDir, {}, () => {});
  51. const watcher1 = await watchPath(rootDir, {}, () => {});
  52. expect(watcher0.native).toBe(watcher1.native);
  53. });
  54. it("reuses existing native watchers even while they're still starting", async function() {
  55. const rootDir = await tempMkdir('atom-fsmanager-test-');
  56. const [watcher0, watcher1] = await Promise.all([
  57. watchPath(rootDir, {}, () => {}),
  58. watchPath(rootDir, {}, () => {})
  59. ]);
  60. expect(watcher0.native).toBe(watcher1.native);
  61. });
  62. it("doesn't attach new watchers to a native watcher that's stopping", async function() {
  63. const rootDir = await tempMkdir('atom-fsmanager-test-');
  64. const watcher0 = await watchPath(rootDir, {}, () => {});
  65. const native0 = watcher0.native;
  66. watcher0.dispose();
  67. const watcher1 = await watchPath(rootDir, {}, () => {});
  68. expect(watcher1.native).not.toBe(native0);
  69. });
  70. it('reuses an existing native watcher on a parent directory and filters events', async function() {
  71. const rootDir = await tempMkdir('atom-fsmanager-test-').then(realpath);
  72. const rootFile = path.join(rootDir, 'rootfile.txt');
  73. const subDir = path.join(rootDir, 'subdir');
  74. const subFile = path.join(subDir, 'subfile.txt');
  75. await mkdir(subDir);
  76. // Keep the watchers alive with an undisposed subscription
  77. const rootWatcher = await watchPath(rootDir, {}, () => {});
  78. const childWatcher = await watchPath(subDir, {}, () => {});
  79. expect(rootWatcher.native).toBe(childWatcher.native);
  80. expect(rootWatcher.native.isRunning()).toBe(true);
  81. const firstChanges = Promise.all([
  82. waitForChanges(rootWatcher, subFile),
  83. waitForChanges(childWatcher, subFile)
  84. ]);
  85. await writeFile(subFile, 'subfile\n', { encoding: 'utf8' });
  86. await firstChanges;
  87. const nextRootEvent = waitForChanges(rootWatcher, rootFile);
  88. await writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' });
  89. await nextRootEvent;
  90. });
  91. it('adopts existing child watchers and filters events appropriately to them', async function() {
  92. const parentDir = await tempMkdir('atom-fsmanager-test-').then(realpath);
  93. // Create the directory tree
  94. const rootFile = path.join(parentDir, 'rootfile.txt');
  95. const subDir0 = path.join(parentDir, 'subdir0');
  96. const subFile0 = path.join(subDir0, 'subfile0.txt');
  97. const subDir1 = path.join(parentDir, 'subdir1');
  98. const subFile1 = path.join(subDir1, 'subfile1.txt');
  99. await mkdir(subDir0);
  100. await mkdir(subDir1);
  101. await Promise.all([
  102. writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }),
  103. writeFile(subFile0, 'subfile 0\n', { encoding: 'utf8' }),
  104. writeFile(subFile1, 'subfile 1\n', { encoding: 'utf8' })
  105. ]);
  106. // Begin the child watchers and keep them alive
  107. const subWatcher0 = await watchPath(subDir0, {}, () => {});
  108. const subWatcherChanges0 = waitForChanges(subWatcher0, subFile0);
  109. const subWatcher1 = await watchPath(subDir1, {}, () => {});
  110. const subWatcherChanges1 = waitForChanges(subWatcher1, subFile1);
  111. expect(subWatcher0.native).not.toBe(subWatcher1.native);
  112. // Create the parent watcher
  113. const parentWatcher = await watchPath(parentDir, {}, () => {});
  114. const parentWatcherChanges = waitForChanges(
  115. parentWatcher,
  116. rootFile,
  117. subFile0,
  118. subFile1
  119. );
  120. expect(subWatcher0.native).toBe(parentWatcher.native);
  121. expect(subWatcher1.native).toBe(parentWatcher.native);
  122. // Ensure events are filtered correctly
  123. await Promise.all([
  124. appendFile(rootFile, 'change\n', { encoding: 'utf8' }),
  125. appendFile(subFile0, 'change\n', { encoding: 'utf8' }),
  126. appendFile(subFile1, 'change\n', { encoding: 'utf8' })
  127. ]);
  128. await Promise.all([
  129. subWatcherChanges0,
  130. subWatcherChanges1,
  131. parentWatcherChanges
  132. ]);
  133. });
  134. });
  135. });