Skip to content

Commit

Permalink
update tests and + fix Record build logic
Browse files Browse the repository at this point in the history
  • Loading branch information
e2 committed May 6, 2015
1 parent c6bb44f commit 2f8ad95
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 61 deletions.
2 changes: 0 additions & 2 deletions lib/listen/directory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ def self.scan(queue, sync_record, dir, rel_path, options)

previous = sync_record.dir_entries(dir, rel_path)

record.add_dir(dir, rel_path)

# TODO: use children(with_directory: false)
path = dir + rel_path
current = Set.new(path.children)
Expand Down
45 changes: 26 additions & 19 deletions lib/listen/record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ class Record

def initialize(listener)
@listener = listener
@paths = _auto_hash
end

def add_dir(dir, rel_path)
rel_path = '.' if [nil, '', '.'].include? rel_path
@paths[dir.to_s][rel_path] ||= {}
@paths = {}
end

def update_file(dir, rel_path, data)
Expand All @@ -33,27 +28,21 @@ def file_data(dir, rel_path)
root = @paths[dir.to_s]
dirname, basename = Pathname(rel_path).split.map(&:to_s)
dirname = '.' if [nil, '', '.'].include? dirname
fail "directory not watched: #{dir}" unless root

root[dirname] ||= {}
root[dirname][basename] ||= {}
root[dirname][basename].dup
end

def dir_entries(dir, rel_path)
rel_path = '.' if [nil, '', '.'].include? rel_path.to_s
@paths[dir.to_s][rel_path.to_s] ||= _auto_hash
tree = @paths[dir.to_s][rel_path.to_s]

result = {}
tree.each do |key, values|
# only get data for file entries
result[key] = values.key?(:mtime) ? values : {}
end
result
@paths[dir.to_s][rel_path.to_s] ||= {}
end

def build
start = Time.now.to_f
@paths = _auto_hash
@paths = {}

# TODO: refactor this out (1 Record = 1 watched dir)
listener.directories.each do |directory|
Expand All @@ -68,13 +57,31 @@ def build

private

def _auto_hash
Hash.new { |h, k| h[k] = Hash.new }
# TODO: refactor/refactor out
def add_dir(dir, rel_path)
rel_path = '.' if [nil, '', '.'].include? rel_path
dirname, basename = Pathname(rel_path).split.map(&:to_s)
basename = '.' if [nil, '', '.'].include? basename
root = (@paths[dir.to_s] ||= {})
if [nil, '', '.'].include?(dirname)
entries = (root['.'] || {})
entries.merge!(basename => {}) if basename != '.'
root['.'] = entries
else
root[rel_path] ||= {}
end
end

def _fast_update_file(dir, dirname, basename, data)
root = @paths[dir.to_s]
dirname = '.' if [nil, '', '.'].include? dirname

internal_dirname, internal_key = ::File.split(dirname.to_s)
if internal_dirname == '.' && internal_key != '.'
root[internal_dirname] ||= {}
root[internal_dirname][internal_key] ||= {}
end

root[dirname] ||= {}
root[dirname][basename] = (root[dirname][basename] || {}).merge(data)
end
Expand All @@ -93,7 +100,7 @@ def _fast_unset_path(dir, dirname, basename)
# TODO: test with mixed encoding
def _fast_build(root)
symlink_detector = SymlinkDetector.new
@paths[root] = _auto_hash
@paths[root] = {}
remaining = Queue.new
remaining << Entry.new(root, nil, nil)
_fast_build_dir(remaining, symlink_detector) until remaining.empty?
Expand Down
7 changes: 1 addition & 6 deletions spec/lib/listen/directory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
let(:queue) { instance_double(Change, change: nil) }

let(:async_record) do
instance_double(Record, add_dir: true, unset_path: true)
instance_double(Record, unset_path: true)
end

let(:record) do
Expand Down Expand Up @@ -37,11 +37,6 @@
context 'with empty dir' do
before { allow(dir).to receive(:children) { [] } }

it 'sets record dir path' do
expect(async_record).to receive(:add_dir).with(dir, '.')
described_class.scan(queue, record, dir, '.', options)
end

it "queues changes for file path and dir that doesn't exist" do
expect(queue).to receive(:change).with(:file, dir, 'file.rb')

Expand Down
116 changes: 82 additions & 34 deletions spec/lib/listen/record_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,39 @@ def symlink(hash_or_dir)
end
end

def build_watched_dir(record, dir)
record.send(:add_dir, dir, '.')
end

let(:record) { Listen::Record.new(listener) }
let(:dir) { instance_double(Pathname, to_s: '/dir') }

describe '#update_file' do
before { build_watched_dir(record, dir) }

context 'with path in watched dir' do
it 'sets path by spliting dirname and basename' do
record.update_file(dir, 'file.rb', mtime: 1.1)
expect(record.paths['/dir']).to eq('file.rb' => { mtime: 1.1 })
expect(record.paths['/dir']['.']).to eq('file.rb' => { mtime: 1.1 })
end

it 'sets path and keeps old data not overwritten' do
record.update_file(dir, 'file.rb', foo: 1, bar: 2)
record.update_file(dir, 'file.rb', foo: 3)
watched_dir = record.paths['/dir']
expect(watched_dir).to eq('file.rb' => { foo: 3, bar: 2 })
expect(watched_dir['.']).to eq('file.rb' => { foo: 3, bar: 2 })
end
end

context 'with subdir path' do
it 'sets path by spliting dirname and basename' do
record.send(:add_dir, dir, 'path')
record.update_file(dir, 'path/file.rb', mtime: 1.1)
expect(record.paths['/dir']['path']).to eq('file.rb' => { mtime: 1.1 })
end

it 'sets path and keeps old data not overwritten' do
record.send(:add_dir, dir, 'path')
record.update_file(dir, 'path/file.rb', foo: 1, bar: 2)
record.update_file(dir, 'path/file.rb', foo: 3)
file_data = record.paths['/dir']['path']['file.rb']
Expand All @@ -78,36 +86,40 @@ def symlink(hash_or_dir)
end
end

# TODO: refactor/refactor out
describe '#add_dir' do
it 'sets itself when .' do
record.add_dir(dir, '.')
expect(record.paths['/dir']).to eq({})
record.send(:add_dir, dir, '.')
expect(record.paths['/dir']['.']).to eq({})
end

it 'sets itself when nil' do
record.add_dir(dir, nil)
expect(record.paths['/dir']).to eq({})
record.send(:add_dir, dir, nil)
expect(record.paths['/dir']['.']).to eq({})
end

it 'sets itself when empty' do
record.add_dir(dir, '')
expect(record.paths['/dir']).to eq({})
record.send(:add_dir, dir, '')
expect(record.paths['/dir']['.']).to eq({})
end

it 'correctly sets new directory data' do
record.add_dir(dir, 'path/subdir')
expect(record.paths['/dir']).to eq('path/subdir' => {})
record.send(:add_dir, dir, '.')
record.send(:add_dir, dir, 'path/subdir')
expect(record.paths['/dir']).to eq('.' => {}, 'path/subdir' => {})
end

it 'sets path and keeps old data not overwritten' do
record.add_dir(dir, 'path/subdir')
record.send(:add_dir, dir, '.')
record.send(:add_dir, dir, 'path')
record.send(:add_dir, dir, 'path/subdir')
record.update_file(dir, 'path/subdir/file.rb', mtime: 1.1)
record.add_dir(dir, 'path/subdir')
record.send(:add_dir, dir, 'path/subdir')
record.update_file(dir, 'path/subdir/file2.rb', mtime: 1.2)
record.add_dir(dir, 'path/subdir')
record.send(:add_dir, dir, 'path/subdir')

watched = record.paths['/dir']
expect(watched.keys).to eq ['path/subdir']
expect(watched.keys).to eq ['.', 'path/subdir']
expect(watched['path/subdir'].keys).to eq %w(file.rb file2.rb)

subdir = watched['path/subdir']
Expand All @@ -117,20 +129,22 @@ def symlink(hash_or_dir)
end

describe '#unset_path' do
before { build_watched_dir(record, dir) }

context 'within watched dir' do
context 'when path is present' do
before { record.update_file(dir, 'file.rb', mtime: 1.1) }

it 'unsets path' do
record.unset_path(dir, 'file.rb')
expect(record.paths).to eq('/dir' => {})
expect(record.paths['/dir']).to eq('.' => {})
end
end

context 'when path not present' do
it 'unsets path' do
record.unset_path(dir, 'file.rb')
expect(record.paths).to eq('/dir' => {})
expect(record.paths['/dir']).to eq('.' => {})
end
end
end
Expand All @@ -141,20 +155,21 @@ def symlink(hash_or_dir)

it 'unsets path' do
record.unset_path(dir, 'path/file.rb')
expect(record.paths).to eq('/dir' => { 'path' => {} })
expect(record.paths['/dir']).to eq('.' => {'path' => {}}, 'path' => {} )
end
end

context 'when path not present' do
it 'unsets path' do
record.unset_path(dir, 'path/file.rb')
expect(record.paths).to eq('/dir' => {})
expect(record.paths['/dir']).to eq('.' => {})
end
end
end
end

describe '#file_data' do
before { build_watched_dir(record, dir) }
context 'with path in watched dir' do
context 'when path is present' do
before { record.update_file(dir, 'file.rb', mtime: 1.1) }
Expand Down Expand Up @@ -190,6 +205,8 @@ def symlink(hash_or_dir)
end

describe '#dir_entries' do
before { build_watched_dir(record, dir) }

context 'in watched dir' do
subject { record.dir_entries(dir, '.') }

Expand All @@ -203,8 +220,28 @@ def symlink(hash_or_dir)
end

context 'with subdir/file.rb in record' do
before { record.update_file(dir, 'subdir/file.rb', mtime: 1.1) }
it { should eq('subdir' => {}) }
before do
record.update_file(dir, 'subdir', mtime: 1.0)
record.update_file(dir, 'subdir/file.rb', mtime: 1.1)
end

it do
should eq('subdir' => { mtime: 1.0 })
end
end

context 'with tree and root files in record' do
before do
record.update_file(dir, 'file1.rb', mtime: 1.1)
record.update_file(dir, 'subdir', mtime: 1.2)
record.update_file(dir, 'subdir/file2.rb', mtime: 1.4)
end
it do
should eq(
'file1.rb' => { mtime: 1.1 },
'subdir' => { mtime: 1.2 }
)
end
end
end

Expand Down Expand Up @@ -249,10 +286,12 @@ def symlink(hash_or_dir)
real_directory('/dir1' => [])
real_directory('/dir2' => [])

record.update_file(dir, 'path/file.rb', mtime: 1.1)
build_watched_dir(record, '/dir1')
record.update_file('/dir1', 'path/file.rb', mtime: 1.1)
record.build
expect(record.paths).to eq('/dir1' => {}, '/dir2' => {})
expect(record.file_data(dir, 'path/file.rb')).to be_empty
expect(record.paths['/dir1']).to eq('.' => {})
expect(record.paths['/dir2']).to eq('.' => {})
expect(record.file_data('/dir1', 'path/file.rb')).to be_empty
end

let(:foo_stat) { instance_double(::File::Stat, mtime: 1.0, mode: 0644) }
Expand All @@ -271,8 +310,10 @@ def symlink(hash_or_dir)
expect(record.paths.keys).to eq %w( /dir1 /dir2 )
expect(record.paths['/dir1']).
to eq(
'foo' => { mtime: 1.0, mode: 0644 },
'bar' => { mtime: 2.3, mode: 0755 })
'.' => {
'foo' => { mtime: 1.0, mode: 0644 },
'bar' => { mtime: 2.3, mode: 0755 }
})
end
end

Expand All @@ -287,9 +328,17 @@ def symlink(hash_or_dir)
it 'builds record' do
record.build
expect(record.paths.keys).to eq %w( /dir1 /dir2 )
expect(record.paths['/dir1']).
to eq('foo' => { 'bar' => { mtime: 2.3, mode: 0755 } })
expect(record.paths['/dir2']).to eq({})
expect(record.paths['/dir1']).to eq(
'.' => {'foo' => {}},
'foo' => { 'bar' => { mtime: 2.3, mode: 0755 } }
)
expect(record.paths['/dir2']['.']).to eq({})
end

it 'properly shows list of children' do
record.build
expect(record.dir_entries('/dir1', 'foo')).to eq('bar' => {mtime: 2.3, mode: 0755})
expect(record.dir_entries('/dir1', '.')).to eq('foo' => {})
end
end

Expand All @@ -307,13 +356,12 @@ def symlink(hash_or_dir)
it 'builds record' do
record.build
expect(record.paths.keys).to eq %w( /dir1 /dir2 )
expect(record.paths['/dir1']).
to eq(
'foo' => {},
'foo/bar' => {},
'foo/baz' => {}
expect(record.paths['/dir1']).to eq(
'.' => {'foo' => {}},
'foo/bar' => {},
'foo/baz' => {}
)
expect(record.paths['/dir2']).to eq({})
expect(record.paths['/dir2']).to eq('.' => {})
end
end

Expand Down

0 comments on commit 2f8ad95

Please sign in to comment.