From e8deaae3ac054edf6677fee20fa22204d4643c12 Mon Sep 17 00:00:00 2001 From: Kana Natsuno Date: Mon, 29 Oct 2012 00:00:00 +0000 Subject: [PATCH] Version 1.1.0 - |:Expect-not|: - Show proper messages for failed expectations.- |vspec-custom-matcher|: - Change the naming guideline. - Use snake_case instead of camelCase. - Add new aliases for predefined custom matcheres. - Old aliases are still supported, but deprecated. - Support custom failure message. - See |vspec#customize_matcher()| for the details. - It was not possible to show meaningful messages for failed expectations using custom mathcers with old versions. - Change the syntax to register new matcher. - Old syntax is still supported, but deprecated. - Fix to properly support custom matchers with 2 or more arguments. --- .mduem/cache/Makefile.variables | 4 +- after/indent/vim.vim | 2 +- after/syntax/vim/vspec.vim | 2 +- autoload/vspec.vim | 94 +++++++++++++++++++++++----- bin/vspec | 2 +- doc/tags | 2 + doc/vspec.txt | 72 ++++++++++++++++++---- mduem/Makefile | 2 +- t/builtin-matchers.vim | 14 ++++- t/custom-failure-message.t | 105 ++++++++++++++++++++++++++++++++ t/custom-matchers.vim | 54 ++++++++++++++-- t/expect.vim | 4 +- t/tools.vim | 4 +- 13 files changed, 316 insertions(+), 45 deletions(-) create mode 100644 t/custom-failure-message.t diff --git a/.mduem/cache/Makefile.variables b/.mduem/cache/Makefile.variables index de0a083..4e0fdb1 100644 --- a/.mduem/cache/Makefile.variables +++ b/.mduem/cache/Makefile.variables @@ -1,6 +1,6 @@ -all_files_in_repos := Makefile after/indent/vim.vim after/syntax/vim/vspec.vim autoload/vspec.vim bin/vspec doc/vspec.txt t/after.vim t/before.vim t/builtin-matchers.vim t/check-vspec-result t/context.vim t/custom-matchers.vim t/error-in-describe.t t/error-in-it.t t/error-in-should-evaluating.t t/error-in-should-parsing.t t/error-in-source.t t/expect.vim t/indent.vim t/no-hint.vim t/no-test.vim t/runtimepath.vim t/skip.t t/syntax.vim t/todo.t t/tools.vim +all_files_in_repos := Makefile after/indent/vim.vim after/syntax/vim/vspec.vim autoload/vspec.vim bin/vspec doc/vspec.txt t/after.vim t/before.vim t/builtin-matchers.vim t/check-vspec-result t/context.vim t/custom-failure-message.t t/custom-matchers.vim t/error-in-describe.t t/error-in-it.t t/error-in-should-evaluating.t t/error-in-should-parsing.t t/error-in-source.t t/expect.vim t/indent.vim t/no-hint.vim t/no-test.vim t/runtimepath.vim t/skip.t t/syntax.vim t/todo.t t/tools.vim current_branch := master origin_name := origin origin_uri := ../. repos_name := vim-vspec -version := 1.0.2 +version := 1.1.0 diff --git a/after/indent/vim.vim b/after/indent/vim.vim index 06dc553..1ee9a10 100644 --- a/after/indent/vim.vim +++ b/after/indent/vim.vim @@ -1,5 +1,5 @@ " Vim additional indent settings: vim/vspec - indent vspec commands -" Version: 1.0.2 +" Version: 1.1.0 " Copyright (C) 2012 Kana Natsuno " License: So-called MIT/X license {{{ " Permission is hereby granted, free of charge, to any person obtaining diff --git a/after/syntax/vim/vspec.vim b/after/syntax/vim/vspec.vim index 5209f1b..eecc1d8 100644 --- a/after/syntax/vim/vspec.vim +++ b/after/syntax/vim/vspec.vim @@ -1,5 +1,5 @@ " Vim additional syntax: vim/vspec - highlight vspec commands -" Version: 1.0.2 +" Version: 1.1.0 " Copyright (C) 2010-2012 Kana Natsuno " License: So-called MIT/X license {{{ " Permission is hereby granted, free of charge, to any person obtaining diff --git a/autoload/vspec.vim b/autoload/vspec.vim index a8ceaf2..ce7692d 100644 --- a/autoload/vspec.vim +++ b/autoload/vspec.vim @@ -1,5 +1,5 @@ " vspec - Testing framework for Vim script -" Version: 1.0.2 +" Version: 1.1.0 " Copyright (C) 2009-2012 Kana Natsuno " License: So-called MIT/X license {{{ " Permission is hereby granted, free of charge, to any person obtaining @@ -49,7 +49,7 @@ let s:current_suites = [] "{{{2 let s:custom_matchers = {} "{{{2 -" :: MatcherNameString -> Funcref +" :: MatcherNameString -> Matcher @@ -149,8 +149,13 @@ endfunction -function! vspec#customize_matcher(matcher_name, funcref) "{{{2 - let s:custom_matchers[a:matcher_name] = a:funcref +function! vspec#customize_matcher(matcher_name, maybe_matcher) "{{{2 + 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 @@ -235,11 +240,15 @@ function! vspec#test(specfile_path) "{{{2 \ suite.subject, \ example \ ) - echo '# Expected' i.expr_actual i.expr_matcher i.expr_expected - echo '# Actual value:' string(i.value_actual) - if !s:is_custom_matcher(i.expr_matcher) - echo '# Expected value:' string(i.value_expected) - endif + echo '# Expected' join(filter([ + \ i.expr_actual, + \ i.expr_not, + \ i.expr_matcher, + \ i.expr_expected, + \ ], 'v:val != ""')) + for line in s:generate_failure_message(i) + echo '# ' . line + endfor elseif type ==# 'TODO' echo printf( \ '%s %d - # TODO %s %s', @@ -301,19 +310,21 @@ endfunction -" Predefined custom matchers - toBeFalse "{{{2 +" Predefined custom matchers - to_be_false "{{{2 function! vspec#_matcher_false(value) return type(a:value) == type(0) ? !(a:value) : s:FALSE endfunction +call vspec#customize_matcher('to_be_false', 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) return type(a:value) == type(0) ? !!(a:value) : s:FALSE endfunction +call vspec#customize_matcher('to_be_true', function('vspec#_matcher_true')) call vspec#customize_matcher('toBeTrue', function('vspec#_matcher_true')) @@ -516,7 +527,7 @@ function! s:parse_should_arguments(s, mode) "{{{2 let matcher = string(_matcher) endif if s:is_custom_matcher(_matcher) - let expected = string(_expected) + let expected = '[' . _expected . ']' endif let not = string(_not) endif @@ -589,14 +600,22 @@ let s:VALID_MATCHERS = (s:VALID_MATCHERS_EQUALITY function! s:are_matched(value_actual, expr_matcher, value_expected) "{{{2 if s:is_custom_matcher(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 \ 'vspec:InvalidOperation:Unknown custom matcher - ' \ . string(custom_matcher_name) 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( - \ s:custom_matchers[custom_matcher_name], - \ [a:value_actual] + eval(printf('[%s]', a:value_expected)) + \ Match, + \ [a:value_actual] + a:value_expected, + \ matcher \ ) elseif s:is_equality_matcher(a:expr_matcher) let type_equality = type(a:value_actual) == type(a:value_expected) @@ -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 return a:expr_matcher =~# '^to' endfunction @@ -683,7 +745,7 @@ endfunction let s:RE_SPLIT_AT_MATCHER = \ printf( -\ '\C\v^(.{-})\s+%%((not)\s+)?(%%(%%(%s)[#?]?)|to\w+>)(.*)$', +\ '\C\v^(.{-})\s+%%((not)\s+)?(%%(%%(%s)[#?]?)|to\w+>)\s*(.*)$', \ join( \ map( \ reverse(sort(copy(s:VALID_MATCHERS))), diff --git a/bin/vspec b/bin/vspec index 17004dc..59bbd17 100644 --- a/bin/vspec +++ b/bin/vspec @@ -1,6 +1,6 @@ #!/bin/bash # bin/vspec - Driver script to test Vim script -# Version: 1.0.2 +# Version: 1.1.0 # Copyright (C) 2009-2012 Kana Natsuno # License: So-called MIT/X license {{{ # Permission is hereby granted, free of charge, to any person obtaining diff --git a/doc/tags b/doc/tags index d7fe7b6..c91733f 100644 --- a/doc/tags +++ b/doc/tags @@ -15,6 +15,7 @@ bin/vspec vspec.txt /*bin\/vspec* vspec vspec.txt /*vspec* vspec#call() vspec.txt /*vspec#call()* vspec#customize_matcher() vspec.txt /*vspec#customize_matcher()* +vspec#customize_matcher()-old vspec.txt /*vspec#customize_matcher()-old* vspec#hint() vspec.txt /*vspec#hint()* vspec#ref() vspec.txt /*vspec#ref()* vspec#set() vspec.txt /*vspec#set()* @@ -29,6 +30,7 @@ vspec-changelog-0.0.4 vspec.txt /*vspec-changelog-0.0.4* vspec-changelog-1.0.0 vspec.txt /*vspec-changelog-1.0.0* vspec-changelog-1.0.1 vspec.txt /*vspec-changelog-1.0.1* vspec-changelog-1.0.2 vspec.txt /*vspec-changelog-1.0.2* +vspec-changelog-1.1.0 vspec.txt /*vspec-changelog-1.1.0* vspec-commands vspec.txt /*vspec-commands* vspec-contents vspec.txt /*vspec-contents* vspec-custom-matcher vspec.txt /*vspec-custom-matcher* diff --git a/doc/vspec.txt b/doc/vspec.txt index 74483a7..d564da0 100644 --- a/doc/vspec.txt +++ b/doc/vspec.txt @@ -1,6 +1,6 @@ *vspec.txt* Testing framework for Vim script -Version 1.0.2 +Version 1.1.0 Script ID: 3012 Copyright (C) 2009-2012 Kana Natsuno License: So-called MIT/X license {{{ @@ -143,26 +143,32 @@ COMMANDS *vspec-commands* Examples: > - function! True(actual_value) + function! ToBeTrue(actual_value) return (type(a:actual_value) == type(0) \ ? a:actual_value \ : !!0) endfunction - call vspec#customize_matcher('toBeTrue', - \ function('True')) + call vspec#customize_matcher( + \ 'to_be_true', + \ function('ToBeTrue') + \ ) - :Expect 123 toBeTrue + :Expect 123 to_be_true " ===> good - :Expect [123] toBeTrue + :Expect [123] to_be_true " ===> bad < *vspec-predefined-custom-matchers* The following custom matcheres are predefined: - "toBeTrue" + "to_be_true" Return true if {actual} value is true. - "toBeFalse" + "to_be_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 {custom-matcher} [{arg}, ...] @@ -240,9 +246,37 @@ vspec#call({funcname}, [{arg}, ...]) *vspec#call()* {arg} is an arbitrary value which is given to the function corresponding to o{funcname}. -vspec#customize_matcher({alias}, {function}) *vspec#customize_matcher()* - Register {function} as a |vspec-custom-matcher| which - alias is {alias}. +vspec#customize_matcher({alias}, {matcher}) *vspec#customize_matcher()* + Register {matcher} as a |vspec-custom-matcher| with + 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()* Tell vspec "hint" information to use useful API to @@ -321,6 +355,22 @@ ETC ~ ============================================================================== CHANGELOG *vspec-changelog* +1.1.0 2012-10-29T22:19:45+09:00 *vspec-changelog-1.1.0* + - |:Expect-not|: + - Show proper messages for failed expectations. + - |vspec-custom-matcher|: + - Change the naming guideline. + - Use snake_case instead of camelCase. + - Add new aliases for predefined custom matcheres. + - Old aliases are still supported, but deprecated. + - Support custom failure message. + - See |vspec#customize_matcher()| for the details. + - It was not possible to show meaningful messages for failed + expectations using custom mathcers with old versions. + - Change the syntax to register new matcher. + - Old syntax is still supported, but deprecated. + - Fix to properly support custom matchers with 2 or more arguments. + 1.0.2 2012-02-12T21:02:50+09:00 *vspec-changelog-1.0.2* - |bin/vspec|: - Remove user's |after-directory| from 'runtimepath' to avoid diff --git a/mduem/Makefile b/mduem/Makefile index 312da2b..bea4e18 100644 --- a/mduem/Makefile +++ b/mduem/Makefile @@ -258,7 +258,7 @@ $(2): $(1) @mkdir -p '$(dir $(2))' @cp '--preserve=mode,ownership' '$(1)' '$(2)' ifeq '$(call SHOULD_INSTALL_ASIS_P,$(1))' '' - @sed -i -e 's/1.0.2/$(version)/' '$(2)' + @sed -i -e 's/1.1.0/$(version)/' '$(2)' endif endef diff --git a/t/builtin-matchers.vim b/t/builtin-matchers.vim index 863fba4..3a34be8 100644 --- a/t/builtin-matchers.vim +++ b/t/builtin-matchers.vim @@ -844,15 +844,25 @@ describe 'isnot?' end end -describe 'be false' +describe 'to_be_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 1 not toBeFalse end end -describe 'be true' +describe 'to_be_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 1 toBeTrue end diff --git a/t/custom-failure-message.t b/t/custom-failure-message.t new file mode 100644 index 0000000..e6c04a9 --- /dev/null +++ b/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 diff --git a/t/custom-matchers.vim b/t/custom-matchers.vim index c74da4a..430254e 100644 --- a/t/custom-matchers.vim +++ b/t/custom-matchers.vim @@ -1,16 +1,58 @@ +call vspec#hint({'scope': 'vspec#scope()'}) + describe 'vspec#customize_matcher' - it 'should define a custom matcher' + before + let b:custom_matchers = copy(Ref('s:custom_matchers')) + end + + after + call Set('s:custom_matchers', b:custom_matchers) + end + + it 'still supports old style usage' + let caught = !!0 + try + Expect [] to_be_empty + let caught = !!0 + catch /^vspec:InvalidOperation:Unknown custom matcher - 'to_be_empty'$/ + let caught = !0 + endtry + Expect caught to_be_true + + call vspec#customize_matcher('to_be_empty', function('empty')) + + Expect [] to_be_empty + end + + it 'supports new style usage' let caught = !!0 try - Expect [] toBeEmpty + Expect [] to_be_empty let caught = !!0 - catch /^vspec:InvalidOperation:Unknown custom matcher - 'toBeEmpty'$/ + catch /^vspec:InvalidOperation:Unknown custom matcher - 'to_be_empty'$/ let caught = !0 endtry - Expect caught toBeTrue + Expect caught to_be_true + + let to_be_empty = {} + function! to_be_empty.match(actual) + return empty(a:actual) + endfunction + call vspec#customize_matcher('to_be_empty', to_be_empty) + + Expect [] to_be_empty + end - call vspec#customize_matcher('toBeEmpty', function('empty')) + it 'rejects matchers without required members' + call vspec#customize_matcher('to_be_empty', {}) - Expect [] toBeEmpty + let caught = !!0 + try + Expect [] to_be_empty + let caught = !!0 + catch /^vspec:InvalidOperation:Custom matcher does not have match function - 'to_be_empty'$/ + let caught = !0 + endtry + Expect caught to_be_true end end diff --git a/t/expect.vim b/t/expect.vim index f487b49..5e5c2d2 100644 --- a/t/expect.vim +++ b/t/expect.vim @@ -11,7 +11,7 @@ describe ':Expect' catch /^vspec:ExpectationFailure:/ let is_succeeded = !!0 endtry - Expect is_succeeded toBeFalse + Expect is_succeeded to_be_false end it 'should parse "string" without errors' @@ -36,6 +36,6 @@ describe ':ExpectNot' catch /^vspec:ExpectationFailure:/ let is_succeeded = !!0 endtry - Expect is_succeeded toBeFalse + Expect is_succeeded to_be_false end end diff --git a/t/tools.vim b/t/tools.vim index b52b768..6048aed 100644 --- a/t/tools.vim +++ b/t/tools.vim @@ -24,8 +24,8 @@ end describe 'Call' it 'should call a script-local function' - Expect Call('s:is_matcher', '==') toBeTrue - Expect Call('s:is_matcher', '=?') toBeFalse + Expect Call('s:is_matcher', '==') to_be_true + Expect Call('s:is_matcher', '=?') to_be_false end it 'should call a non-script-local function'