Skip to content

Commit

Permalink
fix(file_list): Incorrect response after remove and add file
Browse files Browse the repository at this point in the history
Remove and then immediately add same file can lead to
incorrect content in response. Because pending timeout after remove
may resolve early then file will be processed, and in response get not
processed content.
  • Loading branch information
maksimr committed Jun 3, 2014
1 parent 9ef1c81 commit 0dbc020
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 10 deletions.
20 changes: 11 additions & 9 deletions lib/file_list.js
Expand Up @@ -111,9 +111,7 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) {
};

var resolveDeferred = function(files) {
if (pendingTimeout) {
clearTimeout(pendingTimeout);
}
clearPendingTimeout();

if (!errors.length) {
pendingDeferred.resolve(files || resolveFiles(self.buckets));
Expand All @@ -125,9 +123,7 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) {
};

var fireEventAndDefer = function() {
if (pendingTimeout) {
clearTimeout(pendingTimeout);
}
clearPendingTimeout();

if (!pendingDeferred) {
pendingDeferred = q.defer();
Expand All @@ -137,6 +133,12 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) {
pendingTimeout = setTimeout(resolveDeferred, batchInterval);
};

var clearPendingTimeout = function() {
if (pendingTimeout) {
clearTimeout(pendingTimeout);
}
};


// re-glob all the patterns
this.refresh = function() {
Expand Down Expand Up @@ -171,9 +173,7 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) {
emitter.emit('file_list_modified', pendingDeferred.promise);
}

if (pendingTimeout) {
clearTimeout(pendingTimeout);
}
clearPendingTimeout();

patterns.forEach(function(patternObject, i) {
var pattern = patternObject.pattern;
Expand Down Expand Up @@ -302,6 +302,8 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) {
var addedFile = new File(path);
buckets[i].push(addedFile);

clearPendingTimeout();

return fs.stat(path, function(err, stat) {
// in the case someone refresh() the list before stat callback
if (self.buckets === buckets) {
Expand Down
31 changes: 30 additions & 1 deletion test/unit/file_list.spec.coffee
Expand Up @@ -589,7 +589,6 @@ describe 'file_list', ->
# Batch Interval processing
#============================================================================
describe 'batch interval', ->

it 'should batch multiple changes within an interval', (done) ->
timeoutSpy = sinon.stub().returns true
globals_ = setTimeout: timeoutSpy
Expand Down Expand Up @@ -631,6 +630,36 @@ describe 'file_list', ->
expect(timeoutSpy).to.have.been.called
timeoutSpy.lastCall.args[0]()

it 'should wait while file preprocessing, if file was deleted and immediately added', (done) ->
clock = sinon.useFakeTimers()

m = mocks.loadFile __dirname + '/../../lib/file_list.js', mocks_, global
list = new m.List patterns('/a.*'), [], emitter, preprocessMock, 1000

refreshListAndThen (files) ->
preprocessMock.reset()

emitter.once 'file_list_modified', (promise) ->
expect(promise).to.be.fulfilled.then((files)->

# expect file will be preprocessed when file list promise
# resolved, else we will get incorrect(not processed) content in response
expect(preprocessMock.callCount).to.equal 1
).should.notify(done)

# Remove and then immediately add file to the bucket
list.removeFile '/a.txt'
list.addFile '/a.txt'

# Timed remove pending timeout
# but processing file after add is not completed
clock.tick(1000)

# Finish processing file
process.nextTick -> process.nextTick ->
clock.tick(1000)

clock.tick()

#============================================================================
# Win Globbing
Expand Down

0 comments on commit 0dbc020

Please sign in to comment.