Skip to content

Commit

Permalink
Merge branch 'custom-failure-message'
Browse files Browse the repository at this point in the history
Close gh-8, gh-11.
  • Loading branch information
kana committed Oct 29, 2012
2 parents 7f1b070 + fb166da commit 707b5cd
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 37 deletions.
92 changes: 77 additions & 15 deletions autoload/vspec.vim
Expand Up @@ -49,7 +49,7 @@ let s:current_suites = [] "{{{2




let s:custom_matchers = {} "{{{2 let s:custom_matchers = {} "{{{2
" :: MatcherNameString -> Funcref " :: MatcherNameString -> Matcher






Expand Down Expand Up @@ -149,8 +149,13 @@ endfunction






function! vspec#customize_matcher(matcher_name, funcref) "{{{2 function! vspec#customize_matcher(matcher_name, maybe_matcher) "{{{2
let s:custom_matchers[a:matcher_name] = a:funcref if type(a:maybe_matcher) == type({})
let matcher = a:maybe_matcher
else
let matcher = {'match': a:maybe_matcher}
endif
let s:custom_matchers[a:matcher_name] = matcher
endfunction endfunction




Expand Down Expand Up @@ -235,11 +240,15 @@ function! vspec#test(specfile_path) "{{{2
\ suite.subject, \ suite.subject,
\ example \ example
\ ) \ )
echo '# Expected' i.expr_actual i.expr_matcher i.expr_expected echo '# Expected' join(filter([
echo '# Actual value:' string(i.value_actual) \ i.expr_actual,
if !s:is_custom_matcher(i.expr_matcher) \ i.expr_not,
echo '# Expected value:' string(i.value_expected) \ i.expr_matcher,
endif \ i.expr_expected,
\ ], 'v:val != ""'))
for line in s:generate_failure_message(i)
echo '# ' . line
endfor
elseif type ==# 'TODO' elseif type ==# 'TODO'
echo printf( echo printf(
\ '%s %d - # TODO %s %s', \ '%s %d - # TODO %s %s',
Expand Down Expand Up @@ -301,19 +310,21 @@ endfunction






" Predefined custom matchers - toBeFalse "{{{2 " Predefined custom matchers - to_be_false "{{{2
function! vspec#_matcher_false(value) function! vspec#_matcher_false(value)
return type(a:value) == type(0) ? !(a:value) : s:FALSE return type(a:value) == type(0) ? !(a:value) : s:FALSE
endfunction endfunction
call vspec#customize_matcher('to_be_false', function('vspec#_matcher_false'))
call vspec#customize_matcher('toBeFalse', function('vspec#_matcher_false')) call vspec#customize_matcher('toBeFalse', function('vspec#_matcher_false'))








" Predefined custom matchers - toBeTrue "{{{2 " Predefined custom matchers - to_be_true "{{{2
function! vspec#_matcher_true(value) function! vspec#_matcher_true(value)
return type(a:value) == type(0) ? !!(a:value) : s:FALSE return type(a:value) == type(0) ? !!(a:value) : s:FALSE
endfunction endfunction
call vspec#customize_matcher('to_be_true', function('vspec#_matcher_true'))
call vspec#customize_matcher('toBeTrue', function('vspec#_matcher_true')) call vspec#customize_matcher('toBeTrue', function('vspec#_matcher_true'))




Expand Down Expand Up @@ -516,7 +527,7 @@ function! s:parse_should_arguments(s, mode) "{{{2
let matcher = string(_matcher) let matcher = string(_matcher)
endif endif
if s:is_custom_matcher(_matcher) if s:is_custom_matcher(_matcher)
let expected = string(_expected) let expected = '[' . _expected . ']'
endif endif
let not = string(_not) let not = string(_not)
endif endif
Expand Down Expand Up @@ -589,14 +600,22 @@ let s:VALID_MATCHERS = (s:VALID_MATCHERS_EQUALITY
function! s:are_matched(value_actual, expr_matcher, value_expected) "{{{2 function! s:are_matched(value_actual, expr_matcher, value_expected) "{{{2
if s:is_custom_matcher(a:expr_matcher) if s:is_custom_matcher(a:expr_matcher)
let custom_matcher_name = a:expr_matcher let custom_matcher_name = a:expr_matcher
if !has_key(s:custom_matchers, custom_matcher_name) let matcher = get(s:custom_matchers, custom_matcher_name, 0)
if matcher is 0
throw throw
\ 'vspec:InvalidOperation:Unknown custom matcher - ' \ 'vspec:InvalidOperation:Unknown custom matcher - '
\ . string(custom_matcher_name) \ . string(custom_matcher_name)
endif endif
let Match = get(matcher, 'match', 0)
if Match is 0
throw
\ 'vspec:InvalidOperation:Custom matcher does not have match function - '
\ . string(custom_matcher_name)
endif
return !!call( return !!call(
\ s:custom_matchers[custom_matcher_name], \ Match,
\ [a:value_actual] + eval(printf('[%s]', a:value_expected)) \ [a:value_actual] + a:value_expected,
\ matcher
\ ) \ )
elseif s:is_equality_matcher(a:expr_matcher) elseif s:is_equality_matcher(a:expr_matcher)
let type_equality = type(a:value_actual) == type(a:value_expected) let type_equality = type(a:value_actual) == type(a:value_expected)
Expand Down Expand Up @@ -625,6 +644,49 @@ endfunction






function! s:generate_default_failure_message(i) "{{{2
return [
\ ' Actual value: ' . string(a:i.value_actual),
\ 'Expected value: ' . string(a:i.value_expected),
\ ]
endfunction




function! s:generate_failure_message(i) "{{{2
let matcher = get(s:custom_matchers, a:i.value_matcher, 0)
if matcher is 0
return s:generate_default_failure_message(a:i)
else
let method_name =
\ a:i.value_not == ''
\ ? 'failure_message_for_should'
\ : 'failure_message_for_should_not'
let Generate = get(
\ matcher,
\ method_name,
\ 0
\ )
if Generate is 0
return s:generate_default_failure_message(a:i)
else
let values = [a:i.value_actual]
if a:i.expr_expected != ''
call extend(values, a:i.value_expected)
endif
let maybe_message = call(Generate, values, matcher)
return
\ type(maybe_message) == type('')
\ ? [maybe_message]
\ : maybe_message
endif
endif
endfunction




function! s:is_custom_matcher(expr_matcher) "{{{2 function! s:is_custom_matcher(expr_matcher) "{{{2
return a:expr_matcher =~# '^to' return a:expr_matcher =~# '^to'
endfunction endfunction
Expand Down Expand Up @@ -683,7 +745,7 @@ endfunction


let s:RE_SPLIT_AT_MATCHER = let s:RE_SPLIT_AT_MATCHER =
\ printf( \ printf(
\ '\C\v^(.{-})\s+%%((not)\s+)?(%%(%%(%s)[#?]?)|to\w+>)(.*)$', \ '\C\v^(.{-})\s+%%((not)\s+)?(%%(%%(%s)[#?]?)|to\w+>)\s*(.*)$',
\ join( \ join(
\ map( \ map(
\ reverse(sort(copy(s:VALID_MATCHERS))), \ reverse(sort(copy(s:VALID_MATCHERS))),
Expand Down
54 changes: 44 additions & 10 deletions doc/vspec.txt
Expand Up @@ -143,26 +143,32 @@ COMMANDS *vspec-commands*


Examples: Examples:
> >
function! True(actual_value) function! ToBeTrue(actual_value)
return (type(a:actual_value) == type(0) return (type(a:actual_value) == type(0)
\ ? a:actual_value \ ? a:actual_value
\ : !!0) \ : !!0)
endfunction endfunction
call vspec#customize_matcher('toBeTrue', call vspec#customize_matcher(
\ function('True')) \ 'to_be_true',
\ function('ToBeTrue')
\ )
:Expect 123 toBeTrue :Expect 123 to_be_true
" ===> good " ===> good
:Expect [123] toBeTrue :Expect [123] to_be_true
" ===> bad " ===> bad
< <
*vspec-predefined-custom-matchers* *vspec-predefined-custom-matchers*
The following custom matcheres are predefined: The following custom matcheres are predefined:


"toBeTrue" "to_be_true"
Return true if {actual} value is true. Return true if {actual} value is true.
"toBeFalse" "to_be_false"
Return true if {actual} value is false. Return true if {actual} value is false.
"toBeTrue"
Same as "to_be_true". Deprecated.
"toBeFalse"
Same as "to_be_false". Deprecated.


:Expect {actual} not {matcher} {expected} *:Expect-not* :Expect {actual} not {matcher} {expected} *:Expect-not*
:Expect {actual} not {custom-matcher} [{arg}, ...] :Expect {actual} not {custom-matcher} [{arg}, ...]
Expand Down Expand Up @@ -240,9 +246,37 @@ vspec#call({funcname}, [{arg}, ...]) *vspec#call()*
{arg} is an arbitrary value which is given to the {arg} is an arbitrary value which is given to the
function corresponding to o{funcname}. function corresponding to o{funcname}.


vspec#customize_matcher({alias}, {function}) *vspec#customize_matcher()* vspec#customize_matcher({alias}, {matcher}) *vspec#customize_matcher()*
Register {function} as a |vspec-custom-matcher| which Register {matcher} as a |vspec-custom-matcher| with
alias is {alias}. a given {alias}. {alias} should be snake_case.

{matcher} is a dictionary with the following items:

"match" (required)
A |Funcref| to determine whether {actual}
value matches to {expected} value. It takes
1 or more arguments. The first argument is
{actual} value given to |:Expect|, and the
rest of arguments are arbitrary {expected}
values. It returns true if {actual} value is
matched to {expected} value, or false
otherwise.

"failure_message_for_should" (optional)
A |Funcref| to generate user friendly message
for failed match with |:Expect|. It takes
arguments the same as "match", and it returns
a string or a list of strings to describe
a failure.

"failure_message_for_should_not" (optional)
Like "failure_message_for_should", but it is
used to generate failure message for
|:Expect-not|.

vspec#customize_matcher({alias}, {function}) *vspec#customize_matcher()-old*
Deprecated. Use |vspec#customize_matcher()| instead.
This style is remiained for backward compatibility.


vspec#hint({info}) *vspec#hint()* vspec#hint({info}) *vspec#hint()*
Tell vspec "hint" information to use useful API to Tell vspec "hint" information to use useful API to
Expand Down
14 changes: 12 additions & 2 deletions t/builtin-matchers.vim
Expand Up @@ -844,15 +844,25 @@ describe 'isnot?'
end end
end end


describe 'be false' describe 'to_be_false'
it 'should succeed if a given value is false' it 'should succeed if a given value is false'
Expect 0 to_be_false
Expect 1 not to_be_false
end

it 'is still available as old style alias'
Expect 0 toBeFalse Expect 0 toBeFalse
Expect 1 not toBeFalse Expect 1 not toBeFalse
end end
end end


describe 'be true' describe 'to_be_true'
it 'should succeed if a given value is true' it 'should succeed if a given value is true'
Expect 0 not to_be_true
Expect 1 to_be_true
end

it 'is still available as old style alias'
Expect 0 not toBeTrue Expect 0 not toBeTrue
Expect 1 toBeTrue Expect 1 toBeTrue
end end
Expand Down
105 changes: 105 additions & 0 deletions t/custom-failure-message.t
@@ -0,0 +1,105 @@
#!/bin/bash

./t/check-vspec-result <(cat <<'END'
let s:to_be_empty = {}
function! s:to_be_empty.match(actual)
return empty(a:actual)
endfunction
function! s:to_be_empty.failure_message_for_should(actual)
return 'Actual value is ' . string(a:actual)
endfunction
function! s:to_be_empty.failure_message_for_should_not(actual)
return 'Actual value is empty'
endfunction
call vspec#customize_matcher('to_be_empty', s:to_be_empty)
let s:to_be_multiple_of = {}
function! s:to_be_multiple_of.match(actual, n)
return a:actual % a:n == 0
endfunction
function! s:to_be_multiple_of.failure_message_for_should(actual, n)
return 'Actual value is ' . string(a:actual) . ', not multiple of ' . a:n
endfunction
function! s:to_be_multiple_of.failure_message_for_should_not(actual, n)
return 'Actual value is ' . string(a:actual) . ', multiple of ' . a:n
endfunction
call vspec#customize_matcher('to_be_multiple_of', s:to_be_multiple_of)
let s:to_be_surrounded = {}
function! s:to_be_surrounded.match(actual, l, r)
return a:actual[0:0] ==# a:l && a:actual[-1:-1] ==# a:r
endfunction
function! s:to_be_surrounded.failure_message_for_should(actual, l, r)
return 'Actual value ' . string(a:actual) . ' is not surrounded by ' . a:l . ' and ' . a:r
endfunction
function! s:to_be_surrounded.failure_message_for_should_not(actual, l, r)
return 'Actual value ' . string(a:actual) . ' is surrounded by ' . a:l . ' and ' . a:r
endfunction
call vspec#customize_matcher('to_be_surrounded', s:to_be_surrounded)
describe 'vspec#customize_matcher'
it 'supports custom failure message for positive case with 0 argument'
let xs = [1]
Expect xs to_be_empty
end
it 'supports custom failure message for negative case with 0 argument'
let xs = []
Expect xs not to_be_empty
end
it 'supports custom failure message for positive case with 1 argument'
let m = 17
let n = 4
Expect m not to_be_multiple_of n
Expect m to_be_multiple_of n
end
it 'supports custom failure message for negative case with 1 argument'
let m = 16
let n = 4
Expect m to_be_multiple_of n
Expect m not to_be_multiple_of n
end
it 'supports custom failure message for positive case with 2 arguments'
let s = '(foo)'
let l = '<'
let r = '>'
Expect s not to_be_surrounded l, r
Expect s to_be_surrounded l, r
end
it 'supports custom failure message for negative case with 2 arguments'
let s = '(foo)'
let l = '('
let r = ')'
Expect s to_be_surrounded l, r
Expect s not to_be_surrounded l, r
end
end
END
) <(cat <<'END'
not ok 1 - vspec#customize_matcher supports custom failure message for positive case with 0 argument
# Expected xs to_be_empty
# Actual value is [1]
not ok 2 - vspec#customize_matcher supports custom failure message for negative case with 0 argument
# Expected xs not to_be_empty
# Actual value is empty
not ok 3 - vspec#customize_matcher supports custom failure message for positive case with 1 argument
# Expected m to_be_multiple_of n
# Actual value is 17, not multiple of 4
not ok 4 - vspec#customize_matcher supports custom failure message for negative case with 1 argument
# Expected m not to_be_multiple_of n
# Actual value is 16, multiple of 4
not ok 5 - vspec#customize_matcher supports custom failure message for positive case with 2 arguments
# Expected s to_be_surrounded l, r
# Actual value '(foo)' is not surrounded by < and >
not ok 6 - vspec#customize_matcher supports custom failure message for negative case with 2 arguments
# Expected s not to_be_surrounded l, r
# Actual value '(foo)' is surrounded by ( and )
1..6
END
)

# vim: filetype=sh

0 comments on commit 707b5cd

Please sign in to comment.