Implement `test_macro' recipe keyword

This commit is contained in:
Thibault Jouan 2014-06-02 10:49:05 +00:00
parent 18b835b10e
commit 3c46c5bc61
9 changed files with 197 additions and 15 deletions

View File

@ -0,0 +1,52 @@
Feature: `test_macro' recipe keyword
Scenario: declares a new test keyword
Given a recipe with:
"""
test_macro :even? do |n|
n % 2 == 0
end
[1, 2].each do |n|
task "test_macro-even-#{n}" do
condition { even? n }
echo n
end
end
"""
When I successfully execute the recipe
Then the output must contain "2"
And the output must not contain "1"
@sshd
Scenario: has access to core tests
Given a recipe with:
"""
target 'some_host.test'
test_macro(:other_env?) { |k| env? k }
[:shell, :non_existent_var].each do |k|
task "test_macro-condition-#{k}" do
condition { other_env? k }
echo "#{k}_ok"
end
end
"""
When I successfully execute the recipe
Then the output must contain "shell_ok"
Then the output must not contain "non_existent_var_ok"
Scenario: has access to other tests declared with `test_macro'
Given a recipe with:
"""
test_macro(:one?) { |e| e == 1 }
test_macro(:one_alias?) { |e| one? e }
task :test_macro_macro do
condition { one_alias? 1 }
echo 'one_alias_ok'
end
"""
When I successfully execute the recipe
Then the output must contain "one_alias_ok"

View File

@ -16,6 +16,7 @@ require 'producer/core/actions/file_writer'
# condition tests (need to be defined before the condition DSL) # condition tests (need to be defined before the condition DSL)
require 'producer/core/test' require 'producer/core/test'
require 'producer/core/tests/condition_test'
require 'producer/core/tests/file_contains' require 'producer/core/tests/file_contains'
require 'producer/core/tests/has_dir' require 'producer/core/tests/has_dir'
require 'producer/core/tests/has_env' require 'producer/core/tests/has_env'

View File

@ -2,9 +2,9 @@ module Producer
module Core module Core
class Condition class Condition
class << self class << self
def evaluate(env, &block) def evaluate(env, *args, &block)
dsl = DSL.new(env, &block) dsl = DSL.new(env, &block)
return_value = dsl.evaluate return_value = dsl.evaluate *args
Condition.new(dsl.tests, return_value) Condition.new(dsl.tests, return_value)
end end
end end

View File

@ -3,12 +3,21 @@ module Producer
class Condition class Condition
class DSL class DSL
class << self class << self
def define_test(keyword, klass) def define_test(keyword, test)
define_method(keyword) do |*args| {
@tests << klass.new(@env, *args) keyword => false,
end "no_#{keyword}" => true
define_method("no_#{keyword}") do |*args| }.each do |kw, negated|
@tests << klass.new(@env, *args, negated: true) define_method(kw) do |*args|
if test.respond_to? :call
args = [test, *args]
klass = Tests::ConditionTest
else
klass = test
end
t = klass.new(@env, *args, negated: negated)
@tests << t
end
end end
end end
end end
@ -29,8 +38,8 @@ module Producer
@tests = [] @tests = []
end end
def evaluate def evaluate(*args)
instance_eval &@block instance_exec *args, &@block
end end
end end
end end

View File

@ -38,6 +38,10 @@ module Producer
end end
end end
def test_macro(name, dsl: Condition::DSL, &block)
dsl.define_test(name, block)
end
def set(key, value) def set(key, value)
env[key] = value env[key] = value
end end

View File

@ -0,0 +1,23 @@
module Producer
module Core
module Tests
class ConditionTest < Test
def verify
condition.met?
end
def condition
Condition.evaluate(env, *condition_args, &condition_block)
end
def condition_args
arguments.drop 1
end
def condition_block
arguments.first
end
end
end
end
end

View File

@ -22,9 +22,9 @@ module Producer::Core
end end
describe '.define_test' do describe '.define_test' do
let(:some_test_class) { Test } let(:some_test) { Test }
before { described_class.define_test(:some_test, some_test_class) } before { described_class.define_test(:some_test, some_test) }
it 'defines a new test keyword' do it 'defines a new test keyword' do
expect(dsl).to respond_to :some_test expect(dsl).to respond_to :some_test
@ -41,19 +41,38 @@ module Producer::Core
it 'registers the test with current env' do it 'registers the test with current env' do
dsl.some_test dsl.some_test
expect(dsl.tests.first.env).to be env expect(dsl.tests.last.env).to be env
end end
it 'registers the test with given arguments' do it 'registers the test with given arguments' do
dsl.some_test :some, :args dsl.some_test :some, :args
expect(dsl.tests.first.arguments).to eq [:some, :args] expect(dsl.tests.last.arguments).to eq [:some, :args]
end
context 'when given test is callable' do
let(:some_test) { proc {} }
before { dsl.some_test }
it 'registers a condition test' do
expect(dsl.tests.last).to be_a Tests::ConditionTest
end
it 'registers the test with given block' do
expect(dsl.tests.last.condition_block).to be some_test
end
it 'registers the test with given arguments' do
dsl.some_test :some, :args
expect(dsl.tests.last.condition_args).to eq [:some, :args]
end
end end
end end
context 'when a negated test keyword is called' do context 'when a negated test keyword is called' do
it 'registers a negated test' do it 'registers a negated test' do
dsl.no_some_test dsl.no_some_test
expect(dsl.tests.first).to be_negated expect(dsl.tests.last).to be_negated
end end
end end
end end
@ -81,6 +100,15 @@ module Producer::Core
it 'returns the value returned by the assigned block' do it 'returns the value returned by the assigned block' do
expect(dsl.evaluate).to eq block.call expect(dsl.evaluate).to eq block.call
end end
context 'when arguments are given' do
let(:block) { proc { |e| throw e } }
it 'passes arguments as block parameters' do
expect { dsl.evaluate :some_argument }
.to throw_symbol :some_argument
end
end
end end
end end
end end

View File

@ -102,6 +102,16 @@ module Producer::Core
end end
end end
describe '#test_macro' do
it 'defines the new test' do
condition_dsl = double 'condition dsl'
test_block = proc {}
expect(condition_dsl)
.to receive(:define_test).with(:some_test, test_block)
dsl.test_macro(:some_test, dsl: condition_dsl, &test_block)
end
end
describe '#set' do describe '#set' do
it 'registers a key/value pair in env registry' do it 'registers a key/value pair in env registry' do
dsl.set :some_key, :some_value dsl.set :some_key, :some_value

View File

@ -0,0 +1,55 @@
require 'spec_helper'
module Producer::Core
module Tests
describe ConditionTest do
let(:env) { double 'env' }
let(:block) { proc { true } }
let(:arguments) { [:some, :args] }
subject(:test) { described_class.new(env, block, *arguments) }
it_behaves_like 'test'
describe '#verify' do
context 'when condition is met' do
it 'returns true' do
expect(test.verify).to be true
end
end
context 'when condition is not met' do
let(:block) { proc { false } }
it 'returns false' do
expect(test.verify).to be false
end
end
end
describe '#condition' do
it 'evaluates a conditon' do
expect(Condition).to receive(:evaluate).with(env, *arguments, &block)
test.condition
end
it 'returns the evaluated condition' do
condition = double 'condition'
allow(Condition).to receive(:evaluate) { condition }
expect(test.condition).to eq condition
end
end
describe '#condition_args' do
it 'returns arguments for condition' do
expect(test.condition_args).to eq arguments
end
end
describe '#condition_block' do
it 'returns condition block' do
expect(test.condition_block).to eq block
end
end
end
end
end