Skip to content

Commit

Permalink
implement #find_files and #find_file
Browse files Browse the repository at this point in the history
  • Loading branch information
taichi-ishitani committed Sep 15, 2023
1 parent 88089a5 commit efc3bdd
Show file tree
Hide file tree
Showing 2 changed files with 298 additions and 9 deletions.
39 changes: 30 additions & 9 deletions lib/flgen/file_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ def initialize(context, path)
@context = context
@path = path
@root_directories = extract_root
@default_search_path = {}
@default_search_path = init_default_search_path
end

def default_search_path(**seach_paths)
@default_search_path.update(seach_paths)
def default_search_path(**search_paths)
@default_search_path.update(search_paths)
end

def reset_default_search_path(*target_types)
Expand Down Expand Up @@ -52,6 +52,17 @@ def library_directory(path, from: nil, raise_error: true)
add_directory_entry(path, from, location, raise_error, :library_directory)
end

def find_files(*patterns, from: nil, &block)
location = caller_location
glob_files(patterns, __method__, from, location)
.then { |e| block ? e.each(&block) : e.to_a }
end

def find_file(*patterns, from: nil)
location = caller_location
glob_files(patterns, __method__, from, location).first
end

def file?(path, from: :current)
location = caller_location
!extract_file_path(path, from, location, :file).nil?
Expand Down Expand Up @@ -96,6 +107,10 @@ def repository_root?(path)
File.exist?(path.join('.git').to_s)
end

def init_default_search_path
Hash.new { |_, key| key == :file_list ? :root : :current }
end

def load_file_list(path, from, location, raise_error)
unless (list_path = extract_file_path(path, from, location, :file_list))
raise_no_entry_error(path, location, raise_error)
Expand Down Expand Up @@ -135,6 +150,17 @@ def add_directory_entry(path, from, location, raise_error, type)
@context.__send__(method, directory_path)
end

def glob_files(patterns, method_name, from, location)
patterns.product(search_root('', from, location, method_name))
.lazy.flat_map { |patten, base| do_glob_files(patten, base) }
end

def do_glob_files(patten, base)
Dir.glob(patten, base: base)
.map { |path| File.join(base, path) }
.select(&File.method(:file?))
end

def caller_location
caller_locations(2, 1).first
end
Expand All @@ -155,13 +181,8 @@ def extract_path(path, from, location, type, checker)

FROM_KEYWORDS = [:cwd, :current, :local_root, :root].freeze

DEFAULT_SEARCH_PATH = {
file_list: :root, source_file: :current, library_file: :current, file: :current,
include_directory: :current, library_directory: :current, directory: :current
}.freeze

def search_root(path, from, location, type)
search_path = from || @default_search_path[type] || DEFAULT_SEARCH_PATH[type]
search_path = from || @default_search_path[type]
if absolute_path?(path)
['']
elsif FROM_KEYWORDS.include?(search_path)
Expand Down
268 changes: 268 additions & 0 deletions spec/flgen/file_list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,274 @@ def setup_expectation(root, dir, valid = true)
end
end

describe '#find_files/#find_file' do
def mock_glab(patten, results)
results.each do |(base, paths)|
allow(Dir).to receive(:glob).with(patten, base: base).and_return(Array(paths))
Array(paths).each do |path|
allow(File).to receive(:file?).with(File.join(base, path)).and_return(true)
end
end
end

let(:file_names) do
['foo.sv', 'bar.sv', 'baz.v', 'qux.v']
end

before do
allow(Dir).to receive(:glob).with(any_args).and_call_original
end

it '呼び出し元を起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)

mock_glab('*.sv', { __dir__ => file_names[0..1] })
expect(file_list.find_file('*.sv'))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])
expect(file_list.find_file('*.v')).to be_nil
expect(file_list.find_files('*.v')).to be_empty

mock_glab('*.v' , { __dir__ => file_names[2..3] })
expect(file_list.find_file('*.sv', '*.v'))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv', '*.v'))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1]),
File.join(__dir__, file_names[2]),
File.join(__dir__, file_names[3])
])
end

context 'from: :rootが指定された場合' do
it 'ルートディレクトリを起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)

mock_glab('*.sv', { root_directories[0] => file_names[0], root_directories[1] => file_names[1] })
expect(file_list.find_file('*.sv', from: :root))
.to eq File.join(root_directories[0], file_names[0])
expect(file_list.find_files('*.sv', from: :root))
.to match([
File.join(root_directories[0], file_names[0]),
File.join(root_directories[1], file_names[1])
])
expect(file_list.find_file('*.v', from: :root)).to be_nil
expect(file_list.find_files('*.v', from: :root)).to be_empty

mock_glab('*.v', { root_directories[0] => file_names[2], root_directories[1] => file_names[3] })
expect(file_list.find_file('*.sv', '*.v', from: :root))
.to eq File.join(root_directories[0], file_names[0])
expect(file_list.find_files('*.sv', '*.v', from: :root))
.to match([
File.join(root_directories[0], file_names[0]),
File.join(root_directories[1], file_names[1]),
File.join(root_directories[0], file_names[2]),
File.join(root_directories[1], file_names[3])
])
end
end

context 'from: :local_rootが指定された場合' do
it '直近のルートディレクトリを起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)

mock_glab('*.sv', { root_directories.last => file_names[0..1] })
expect(file_list.find_file('*.sv', from: :local_root))
.to eq File.join(root_directories.last, file_names[0])
expect(file_list.find_files('*.sv', from: :local_root))
.to match([
File.join(root_directories.last, file_names[0]),
File.join(root_directories.last, file_names[1])
])
expect(file_list.find_file('*.v', from: :local_root)).to be_nil
expect(file_list.find_files('*.v', from: :local_root)).to be_empty

mock_glab('*.v', { root_directories.last => file_names[2..3] })
expect(file_list.find_file('*.sv', '*.v', from: :local_root))
.to eq File.join(root_directories.last, file_names[0])
expect(file_list.find_files('*.sv', '*.v', from: :local_root))
.to match([
File.join(root_directories.last, file_names[0]),
File.join(root_directories.last, file_names[1]),
File.join(root_directories.last, file_names[2]),
File.join(root_directories.last, file_names[3])
])
end
end

context 'from: :currentが指定された場合' do
it '呼び出し元を起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)

mock_glab('*.sv', { __dir__ => file_names[0..1] })
expect(file_list.find_file('*.sv', from: :current))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv', from: :current))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])
expect(file_list.find_file('*.v', from: :current)).to be_nil
expect(file_list.find_files('*.v', from: :current)).to be_empty

mock_glab('*.v' , { __dir__ => file_names[2..3] })
expect(file_list.find_file('*.sv', '*.v', from: :current))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv', '*.v', from: :current))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1]),
File.join(__dir__, file_names[2]),
File.join(__dir__, file_names[3])
])
end
end

context 'from: :cwdが指定された場合' do
it '実行ディレクトリを起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)

mock_cwd
mock_glab('*.sv', { __dir__ => file_names[0..1] })
expect(file_list.find_file('*.sv', from: :cwd))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv', from: :cwd))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])
expect(file_list.find_file('*.v', from: :cwd)).to be_nil
expect(file_list.find_files('*.v', from: :cwd)).to be_empty

mock_cwd
mock_glab('*.v' , { __dir__ => file_names[2..3] })
expect(file_list.find_file('*.sv', '*.v', from: :cwd))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv', '*.v', from: :cwd))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1]),
File.join(__dir__, file_names[2]),
File.join(__dir__, file_names[3])
])
end
end

context 'from:でベースディレクトリが指定された場合' do
it 'ベースディレクトリを起点として、入力パターンに一致するファイル一覧を返す' do
file_list = described_class.new(context, path)
base = non_root_directories.sample

mock_glab('*.sv', { base => file_names[0..1] })
expect(file_list.find_file('*.sv', from: base))
.to eq File.join(base, file_names[0])
expect(file_list.find_files('*.sv', from: base))
.to match([
File.join(base, file_names[0]),
File.join(base, file_names[1])
])
expect(file_list.find_file('*.v', from: base)).to be_nil
expect(file_list.find_files('*.v', from: base)).to be_empty

mock_glab('*.v' , { base => file_names[2..3] })
expect(file_list.find_file('*.sv', '*.v', from: base))
.to eq File.join(base, file_names[0])
expect(file_list.find_files('*.sv', '*.v', from: base))
.to match([
File.join(base, file_names[0]),
File.join(base, file_names[1]),
File.join(base, file_names[2]),
File.join(base, file_names[3])
])
end
end

specify '#default_search_pathでfrom:未指定時の挙動を変更できる' do
file_list = file_list = described_class.new(context, path)

mock_glab('*.sv', { root_directories[0] => file_names[0], root_directories[1] => file_names[1] })
file_list.default_search_path(find_files: :root, find_file: :root)
expect(file_list.find_file('*.sv'))
.to eq File.join(root_directories[0], file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(root_directories[0], file_names[0]),
File.join(root_directories[1], file_names[1])
])

mock_glab('*.sv', { root_directories.last => file_names[0..1] })
file_list.default_search_path(find_files: :local_root, find_file: :local_root)
expect(file_list.find_file('*.sv'))
.to eq File.join(root_directories.last, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(root_directories.last, file_names[0]),
File.join(root_directories.last, file_names[1])
])

mock_glab('*.sv', { __dir__ => file_names[0..1] })
file_list.default_search_path(find_files: :current, find_file: :current)
expect(file_list.find_file('*.sv'))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])

mock_cwd
mock_glab('*.sv', { __dir__ => file_names[0..1] })
file_list.default_search_path(find_files: :cwd, find_file: :cwd)
expect(file_list.find_file('*.sv'))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])

base = non_root_directories.sample
mock_glab('*.sv', { base => file_names[0..1] })
file_list.default_search_path(find_files: base, find_file: base)
expect(file_list.find_file('*.sv'))
.to eq File.join(base, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(base, file_names[0]),
File.join(base, file_names[1])
])

mock_glab('*.sv', { __dir__ => file_names[0..1] })
file_list.reset_default_search_path(:find_files, :find_file)
expect(file_list.find_file('*.sv'))
.to eq File.join(__dir__, file_names[0])
expect(file_list.find_files('*.sv'))
.to match([
File.join(__dir__, file_names[0]),
File.join(__dir__, file_names[1])
])
end

context 'ブロックを与えた場合' do
specify '一致したファイルを引数としてブロックを実行する' do
file_list = described_class.new(context, path)

mock_glab('*.sv', { __dir__ => file_names[0..1] })
expect { |b|
file_list.find_files('*.sv', &b)
}.to yield_successive_args(*file_names[0..1].map { |f| File.join(__dir__, f) })
expect { |b|
file_list.find_files('.v', &b)
}.not_to yield_control
end
end
end

describe '#file?' do
let(:file_names) do
['foo.sv', 'bar.sv', 'baz.sv']
Expand Down

0 comments on commit efc3bdd

Please sign in to comment.