configuration
local dir
before_each ->
config.define name: 'project_spec_var', description: 'test'
dir = File.tmpdir!
after_each -> dir\delete_all!
loads the project's values from .howl/config.json in the root
(dir / '.howl')\mkdir!
(dir / '.howl/config.json').contents = '{"config": {"project_spec_var": "project"}}'
project = Project dir
assert.equal dir / '.howl/config.json', project.config_file
assert.equal 'project', project.config.project_spec_var
assert.equal 'project', config.for_file(dir / 'sub/file.txt').project_spec_var
saves the values set for the project there with config.save_config()
config.reset!
project = Project dir
project.config.project_spec_var = 'saved'
with_tmpdir (sys_dir) -> config.save_config sys_dir
assert.same {config: {project_spec_var: 'saved'}}, json.decode(project.config_file.contents)
loads the file again when it is saved from a buffer
(dir / '.howl')\mkdir!
project = Project dir
Project.open[dir] = project
buffer = Buffer {}
buffer.file = project.config_file
buffer.text = '{"config": {"project_spec_var": "edited"}}'
buffer\save!
assert.equal 'edited', project.config.project_spec_var
paths()
delegates to .vc.paths() if it is available
vc = paths: -> {'path'}
assert.same vc.paths!, Project(File('/no/such/root'), vc)\paths!
falls back to a FS scan, skipping directories, backup files and hidden exts
orig_exts = config.hidden_file_extensions
config.hidden_file_extensions = {'foo'}
with_tmpdir (dir) ->
regular = dir / 'regular.lua'
regular\touch!
sub_dir = dir / 'sub_dir'
sub_dir\mkdir!
hidden = dir / '.config'
hidden\touch!
backup = dir / 'config~'
backup\touch!
hidden_ext = dir / 'bar.foo'
hidden_ext\touch!
paths = Project(dir)\paths!
config.hidden_file_extensions = orig_exts
table.sort paths
assert.same { '.config', 'regular.lua' }, paths
files()
delegates to .vc.files() if it is available
vc = files: -> 'files'
assert.equal vc.files!, Project(File('/no/such/root'), vc)\files!
falls back to a FS scan, skipping directories and backup files
with_tmpdir (dir) ->
regular = dir / 'regular.lua'
regular\touch!
sub_dir = dir / 'sub_dir'
sub_dir\mkdir!
hidden = dir / '.config'
hidden\touch!
backup = dir / 'config~'
backup\touch!
assert.same { regular.path, hidden.path }, [f.path for f in *Project(dir)\files!]
environment
local dir, project
provide = (name, handler, files) ->
Project.register_environment_provider :name, :handler, :files
before_each ->
dir = File.tmpdir!
project = Project dir
after_each ->
Project.unregister_environment_provider 'test-a'
Project.unregister_environment_provider 'test-b'
dir\delete_all!
find_environment()
combines the providers' variables in order, giving each the project and those before it
local args
provide 'test-a', -> { A: '1', B: '1' }
provide 'test-b', (p, vars) ->
args = { p, moon.copy(vars) }
{ B: '2' }
assert.same { A: '1', B: '2' }, project\find_environment!
assert.equals project, args[1]
assert.same { A: '1', B: '1' }, args[2]
skips providers that fail, logging the error
provide 'test-a', -> error 'no environment here'
provide 'test-b', -> { B: '2' }
assert.same { B: '2' }, project\find_environment!
assert.includes log.last_error.message, 'no environment here'
adds the variables of project_env_file last, expanding references to earlier ones
provide 'test-a', -> { A: '1', B: '1' }
(dir / '.env').contents = 'B=2\nC=${A}${HOME}\n'
assert.same { A: '1', B: '2', C: "1#{sys.env.HOME}" }, project\find_environment!
uses the file named by project_env_file, and none when it is blank
(dir / '.env').contents = 'A=1'
(dir / 'dev.env').contents = 'B=2'
project.config.project_env_file = 'dev.env'
assert.same { B: '2' }, project\find_environment!
project = Project dir
project.config.project_env_file = ''
assert.same {}, project\find_environment!
keeps the variables in .environment, running the providers once
handler = spy.new -> { A: '1' }
provide 'test-a', handler
vars = project\find_environment!
assert.equals vars, project.environment
assert.equals vars, project\find_environment!
assert.spy(handler).was_called(1)
makes callers wait while the variables are being found
handle = dispatch.park 'test-provider'
provide 'test-a', ->
dispatch.wait handle
{ A: '1' }
local first, second
dispatch.launch -> first = project\find_environment!
dispatch.launch -> second = project\find_environment!
assert.is_nil first
dispatch.resume handle
assert.same { A: '1' }, first
assert.equals first, second
process_env()
returns nil when there are no variables
assert.is_nil project\process_env!
returns Howl's environment with the variables on top
provide 'test-a', -> { HOME: '/elsewhere', HOWL_PROJECT_VAR: '1' }
env = project\process_env!
assert.equals '/elsewhere', env.HOME
assert.equals '1', env.HOWL_PROJECT_VAR
assert.equals sys.env.PATH, env.PATH
find_executable(name)
local bin
before_each ->
bin = dir / 'bin'
bin\mkdir!
(bin / 'howl-project-tool').contents = ''
looks in the PATH of the project's environment
provide 'test-a', -> { PATH: "#{bin.path}:#{sys.env.PATH}" }
assert.equals (bin / 'howl-project-tool').path, project\find_executable 'howl-project-tool'
assert.equals sys.find_executable('sh'), project\find_executable 'sh'
uses Howl's PATH when the environment sets none
assert.is_nil project\find_executable 'howl-project-tool'
assert.equals sys.find_executable('sh'), project\find_executable 'sh'
keeps what it found until the environment is found again
provide 'test-a', -> { PATH: bin.path }
assert.is_nil project\find_executable 'howl-later-tool'
(bin / 'howl-later-tool').contents = ''
assert.is_nil project\find_executable 'howl-later-tool'
project.environment = nil
assert.equals (bin / 'howl-later-tool').path, project\find_executable 'howl-later-tool'
(when a file is saved)
local value, buffer
save = (name) ->
buffer.file = dir / name
buffer\save!
before_each ->
value = '1'
provide 'test-a', (-> { A: value }), { 'deps.txt' }
Project.open[dir] = project
buffer = Buffer {}
finds the environment again when it depends on the file, signalling any change
project\find_environment!
with_signal_handler 'project-environment-changed', nil, (handler) ->
save 'deps.txt'
assert.spy(handler).was_not_called!
value = '2'
save 'deps.txt'
assert.spy(handler).was_called_with { :project }
(dir / '.env').contents = 'B=3'
save '.env'
assert.spy(handler).was_called(2)
assert.same { A: '2', B: '3' }, project.environment
leaves it alone for other files, or when it is not yet found
save 'deps.txt'
assert.is_nil project.environment
vars = project\find_environment!
value = '2'
save 'other.txt'
(dir / 'sub')\mkdir!
save 'sub/deps.txt'
assert.equals vars, project.environment