From 2f8ad95170d122f0179135938f8476208dc05e57 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Tue, 5 May 2015 17:23:21 +0200 Subject: [PATCH] update tests and + fix Record build logic --- lib/listen/directory.rb | 2 - lib/listen/record.rb | 45 +++++++----- spec/lib/listen/directory_spec.rb | 7 +- spec/lib/listen/record_spec.rb | 116 +++++++++++++++++++++--------- 4 files changed, 109 insertions(+), 61 deletions(-) diff --git a/lib/listen/directory.rb b/lib/listen/directory.rb index e1185b4f..8787057c 100644 --- a/lib/listen/directory.rb +++ b/lib/listen/directory.rb @@ -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) diff --git a/lib/listen/record.rb b/lib/listen/record.rb index 44cc1684..6c5a3b03 100644 --- a/lib/listen/record.rb +++ b/lib/listen/record.rb @@ -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) @@ -33,6 +28,8 @@ 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 @@ -40,20 +37,12 @@ def file_data(dir, rel_path) 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| @@ -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 @@ -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? diff --git a/spec/lib/listen/directory_spec.rb b/spec/lib/listen/directory_spec.rb index f77e98ce..46f0f6d0 100644 --- a/spec/lib/listen/directory_spec.rb +++ b/spec/lib/listen/directory_spec.rb @@ -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 @@ -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') diff --git a/spec/lib/listen/record_spec.rb b/spec/lib/listen/record_spec.rb index 8e2ea9e0..14aa954e 100644 --- a/spec/lib/listen/record_spec.rb +++ b/spec/lib/listen/record_spec.rb @@ -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'] @@ -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'] @@ -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 @@ -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) } @@ -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, '.') } @@ -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 @@ -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) } @@ -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 @@ -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 @@ -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