├── .travis.yml ├── src ├── tap │ ├── version.cr │ ├── util.cr │ ├── output.cr │ └── t.cr ├── tap.cr └── inline.cr ├── shard.yml ├── .gitignore ├── Makefile ├── spec ├── fizzbuzz.cr ├── fizzbuzz.txt ├── test.cr └── expected.txt ├── LICENSE.md └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | script: 3 | - make spec 4 | -------------------------------------------------------------------------------- /src/tap/version.cr: -------------------------------------------------------------------------------- 1 | module Tap 2 | VERSION = "1.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /src/tap.cr: -------------------------------------------------------------------------------- 1 | require "./tap/*" 2 | 3 | T.begin 4 | at_exit do 5 | exit T.end 6 | end 7 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: tap 2 | version: 1.0.0 3 | description: TAP (Test Anything Protocol) test framework for Crystal 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /libs/ 3 | /.crystal/ 4 | /.shards/ 5 | 6 | 7 | # Libraries don't need dependency lock 8 | # Dependencies will be locked in application that uses them 9 | /shard.lock 10 | 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: spec 2 | spec: 3 | crystal run ./spec/test.cr | diff ./spec/expected.txt - 4 | crystal run ./spec/fizzbuzz.cr 5 | env CRYSTAL_TEST=yes crystal run ./spec/fizzbuzz.cr | diff ./spec/fizzbuzz.txt - 6 | -------------------------------------------------------------------------------- /src/inline.cr: -------------------------------------------------------------------------------- 1 | require "./tap/*" 2 | 3 | {% if env("CRYSTAL_TEST") %} 4 | 5 | def inline_test(name = "") 6 | ::T.test name do 7 | yield 8 | end 9 | end 10 | 11 | T.begin 12 | at_exit do 13 | exit T.end 14 | end 15 | {% else %} 16 | macro inline_test(name = ""); end 17 | {% end %} 18 | -------------------------------------------------------------------------------- /src/tap/util.cr: -------------------------------------------------------------------------------- 1 | # :nodoc: 2 | module Tap::Util 3 | extend self 4 | 5 | macro expr_to_s(expr) 6 | {{ expr.stringify }} 7 | end 8 | 9 | def fix_path(path) 10 | cwd = Dir.current 11 | if path.starts_with?(cwd) 12 | path.sub(cwd, ".") 13 | else 14 | path 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/fizzbuzz.cr: -------------------------------------------------------------------------------- 1 | require "../src/inline" 2 | 3 | def fizzbuzz(n) 4 | return :FizzBuzz if n % 15 == 0 5 | return :Fizz if n % 3 == 0 6 | return :Buzz if n % 5 == 0 7 | n 8 | end 9 | 10 | inline_test :fizzbuzz do 11 | T.eq fizzbuzz(1), 1 12 | T.eq fizzbuzz(2), 2 13 | T.eq fizzbuzz(3), :Fizz 14 | T.eq fizzbuzz(4), 4 15 | T.eq fizzbuzz(5), :Buzz 16 | T.eq fizzbuzz(6), :Fizz 17 | T.eq fizzbuzz(7), 7 18 | T.eq fizzbuzz(8), 8 19 | T.eq fizzbuzz(9), :Fizz 20 | T.eq fizzbuzz(10), :Buzz 21 | T.eq fizzbuzz(11), 11 22 | T.eq fizzbuzz(12), :Fizz 23 | T.eq fizzbuzz(13), 13 24 | T.eq fizzbuzz(14), 14 25 | T.eq fizzbuzz(15), :FizzBuzz 26 | 27 | T.eq fizzbuzz(100), :Buzz 28 | T.eq fizzbuzz(100), :FizzBuzz 29 | T.eq fizzbuzz(111), :Fizz 30 | T.eq fizzbuzz(222), :Fizz 31 | T.eq fizzbuzz(333), :Fizz 32 | T.eq fizzbuzz(444), :Fizz 33 | T.eq fizzbuzz(555), :FizzBuzz 34 | T.eq fizzbuzz(1515), :Fizz 35 | T.eq fizzbuzz(1515), :Buzz 36 | T.eq fizzbuzz(1515), :FizzBuzz 37 | end 38 | -------------------------------------------------------------------------------- /src/tap/output.cr: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | module Tap 4 | TAP_VERSION = 13 5 | 6 | class Output 7 | def initialize(@io : IO); end 8 | 9 | def version 10 | @io.puts "TAP Version #{Tap::TAP_VERSION}" 11 | end 12 | 13 | # Output 1..count 14 | def count(count) 15 | @io.puts "\n1..#{count}" 16 | end 17 | 18 | def comment(comment) 19 | if comment.empty? 20 | @io.puts "#" 21 | end 22 | comment.lines.each do |line| 23 | @io.puts "# #{line.chomp}" 24 | end 25 | end 26 | 27 | def info(info : Array({String, String})) 28 | @io.puts " ---" 29 | info.each do |name_and_value| 30 | name, value = name_and_value 31 | 32 | if value =~ /[-:?\n\r\t]/ 33 | value = ":-\n#{value.lines.map { |l| " #{l}" }.join "\n"}\n" 34 | end 35 | 36 | @io.puts " #{name}: #{value}" 37 | end 38 | @io.puts " ..." 39 | end 40 | 41 | def result(ok, count, message) 42 | @io.puts "#{ok ? "ok" : "not ok"} #{count} - #{message}" 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2015 TSUYUSATO Kitsune 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /spec/fizzbuzz.txt: -------------------------------------------------------------------------------- 1 | TAP Version 13 2 | # fizzbuzz 3 | ok 1 - should equal 4 | ok 2 - should equal 5 | ok 3 - should equal 6 | ok 4 - should equal 7 | ok 5 - should equal 8 | ok 6 - should equal 9 | ok 7 - should equal 10 | ok 8 - should equal 11 | ok 9 - should equal 12 | ok 10 - should equal 13 | ok 11 - should equal 14 | ok 12 - should equal 15 | ok 13 - should equal 16 | ok 14 - should equal 17 | ok 15 - should equal 18 | ok 16 - should equal 19 | not ok 17 - should equal 20 | --- 21 | test: :- 22 | (:FizzBuzz) == (fizzbuzz(100)) 23 | operator: eq 24 | expected: :- 25 | :FizzBuzz 26 | actual: :- 27 | :Buzz 28 | at: :- 29 | ./spec/fizzbuzz.cr - line at 28 30 | ... 31 | ok 18 - should equal 32 | ok 19 - should equal 33 | ok 20 - should equal 34 | ok 21 - should equal 35 | ok 22 - should equal 36 | not ok 23 - should equal 37 | --- 38 | test: :- 39 | (:Fizz) == (fizzbuzz(1515)) 40 | operator: eq 41 | expected: :- 42 | :Fizz 43 | actual: :- 44 | :FizzBuzz 45 | at: :- 46 | ./spec/fizzbuzz.cr - line at 34 47 | ... 48 | not ok 24 - should equal 49 | --- 50 | test: :- 51 | (:Buzz) == (fizzbuzz(1515)) 52 | operator: eq 53 | expected: :- 54 | :Buzz 55 | actual: :- 56 | :FizzBuzz 57 | at: :- 58 | ./spec/fizzbuzz.cr - line at 35 59 | ... 60 | ok 25 - should equal 61 | 62 | 1..25 63 | # tests 25 64 | # pass 22 65 | # fail 3 66 | -------------------------------------------------------------------------------- /spec/test.cr: -------------------------------------------------------------------------------- 1 | require "../src/tap" 2 | 3 | T.ok 1 == 1 4 | T.ok 1 == 1, "one is one" 5 | T.ok 1 == 2 6 | T.ok 1 == 2, "one is two" 7 | 8 | T.not_ok 1 == 1 9 | T.not_ok 1 == 1, "one is one" 10 | T.not_ok 1 == 2 11 | T.not_ok 1 == 2, "one is two" 12 | 13 | T.eq 1, 1 14 | T.eq 1, 1, "one is one" 15 | T.eq 1, 2 16 | T.eq 1, 2, "one is two" 17 | 18 | T.not_eq 1, 1 19 | T.not_eq 1, 1, "one is one" 20 | T.not_eq 1, 2 21 | T.not_eq 1, 2, "one is two" 22 | 23 | T.eq_nil nil 24 | T.eq_nil :not_nil 25 | 26 | T.not_eq_nil nil 27 | T.not_eq_nil :not_nil 28 | 29 | T.eq_true true 30 | T.eq_true "true" 31 | 32 | T.eq_false false 33 | T.eq_false "false" 34 | 35 | class Cheat 36 | def initialize(@object_id : Int32); end 37 | 38 | def inspect(io) 39 | io << "Cheat.new(#{object_id.inspect})" 40 | end 41 | 42 | getter object_id 43 | end 44 | 45 | cheat_one = Cheat.new 1 46 | cheat_two = Cheat.new 2 47 | 48 | T.same cheat_one, cheat_one 49 | T.same cheat_one, cheat_one, "one is one" 50 | T.same cheat_one, cheat_two 51 | T.same cheat_one, cheat_two, "one is two" 52 | 53 | T.not_same cheat_one, cheat_one 54 | T.not_same cheat_one, cheat_one, "one is one" 55 | T.not_same cheat_one, cheat_two 56 | T.not_same cheat_one, cheat_two, "one is two" 57 | 58 | T.is cheat_one, Cheat 59 | T.is cheat_one, String 60 | T.is cheat_two, Cheat, "cheat object" 61 | T.is cheat_two, String, "cheat object" 62 | T.is [cheat_one, cheat_two], Array(Cheat) 63 | T.is [cheat_one, cheat_two], Array 64 | T.is "cheat", Cheat 65 | T.is "cheat", String 66 | T.is "cheat", Cheat, "string object" 67 | T.is "cheat", String, "string object" 68 | 69 | T.is_not cheat_one, Cheat 70 | T.is_not cheat_one, String 71 | T.is_not cheat_two, Cheat, "cheat object" 72 | T.is_not cheat_two, String, "cheat object" 73 | T.is_not [cheat_one, cheat_two], Array(Cheat) 74 | T.is_not [cheat_one, cheat_two], Array 75 | T.is_not "cheat", Cheat 76 | T.is_not "cheat", String 77 | T.is_not "cheat", Cheat, "string object" 78 | T.is_not "cheat", String, "string object" 79 | 80 | T.match "hello", "hello" 81 | T.match "hello", "Hello" 82 | T.match "hello", /hello/ 83 | T.match "Hello", /hello/i 84 | T.match "Hello", /hello/i, "match regex" 85 | T.match 1, 1..3 86 | T.match 5, 1..3 87 | 88 | T.not_match "hello", "hello" 89 | T.not_match "hello", "Hello" 90 | T.not_match "hello", /hello/ 91 | T.not_match "Hello", /hello/i 92 | T.not_match "Hello", /hello/i, "match regex" 93 | T.not_match 1, 1..3 94 | T.not_match 5, 1..3 95 | 96 | class Ex < Exception 97 | def inspect(io) 98 | io << "Ex.new(#{message.inspect})" 99 | end 100 | 101 | def inspect_with_backtrace(io) 102 | inspect io 103 | io.puts 104 | io.puts "no backtrace..." 105 | end 106 | end 107 | 108 | T.raises { raise Ex.new "error" } 109 | T.raises(message: "raise an exception") { raise Ex.new "error" } 110 | T.raises { nil } 111 | T.raises(message: "not raise an exception") { nil } 112 | T.raises("error", "raise an exception") { raise Ex.new "error" } 113 | T.raises("exception", "raise an exception") { raise Ex.new "error" } 114 | T.raises(/error/, "raise an exception") { raise Ex.new "error" } 115 | T.raises(/exception/, "raise an exception") { raise Ex.new "error" } 116 | 117 | T.not_raises { raise Ex.new "error" } 118 | T.not_raises("raise an exception") { raise Ex.new "error" } 119 | T.not_raises { nil } 120 | T.not_raises("not raise an exception") { nil } 121 | 122 | T.test do 123 | T.ok true, "in unnamed group" 124 | T.ok false, "in unnamed group" 125 | end 126 | 127 | T.test "named" do 128 | T.ok true, "in named group" 129 | T.ok false, "in named group" 130 | end 131 | 132 | T.test "catch an exception in test group" do 133 | T.ok true 134 | T.ok false 135 | raise Ex.new "error" 136 | T.ok true 137 | T.ok false 138 | end 139 | 140 | T.skip "catch an exception in test group" do 141 | T.ok true 142 | T.ok false 143 | raise Ex.new "error" 144 | T.ok true 145 | T.ok false 146 | end 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tap 2 | 3 | [TAP](https://testanything.org/) test framework for Crystal. 4 | 5 | [![](https://img.shields.io/travis/MakeNowJust/tap.svg?style=flat-square)](https://travis-ci.org/MakeNowJust/tap) 6 | [![docrystal.org](https://img.shields.io/badge/docrystal-ref-866BA6.svg?style=flat-square)](http://docrystal.org/github.com/MakeNowJust/tap) 7 | 8 | ## Installation 9 | 10 | Add this to your application's `shard.yml`: 11 | 12 | ```yaml 13 | dependencies: 14 | tap: 15 | github: MakeNowJust/tap 16 | ``` 17 | 18 | 19 | ## Usage 20 | 21 | ```crystal 22 | require "tap" 23 | 24 | T.test "awesome test name 1" do 25 | T.ok 1 == 1, "one is one" 26 | T.ok 1 == 2, "one is two" 27 | end 28 | 29 | T.test "awesome test name 2" do 30 | T.is "I am", String 31 | T.eq 1, 1, "one is one again" 32 | end 33 | ``` 34 | 35 | then run it: 36 | 37 | ```console 38 | $ crystal run ./test.cr 39 | TAP Version 13 40 | # awesome test name 1 41 | ok 1 one is one 42 | not ok 2 one is two 43 | --- 44 | test: 1 == 1 45 | operator: ok 46 | expected: true 47 | actual: false 48 | at: test.cr - line at 5 49 | ... 50 | # awesome test name 2 51 | ok 3 52 | ok 4 one is one again 53 | 54 | 1..4 55 | # tests 4 56 | # pass 3 57 | # fail 1 58 | ``` 59 | 60 | You can use inline specs: 61 | 62 | ```crystal 63 | require "tap/inline" 64 | 65 | def fizzbuzz(n) 66 | return :FizzBuzz if n % 15 == 0 67 | return :Fizz if n % 3 == 0 68 | return :Buzz if n % 5 == 0 69 | n 70 | end 71 | 72 | inline_test :fizzbuzz do 73 | T.eq fizzbuzz(1), 1 74 | T.eq fizzbuzz(2), 2 75 | T.eq fizzbuzz(3), :Fizz 76 | T.eq fizzbuzz(4), 4 77 | T.eq fizzbuzz(5), :Buzz 78 | T.eq fizzbuzz(6), :Fizz 79 | T.eq fizzbuzz(7), 7 80 | T.eq fizzbuzz(8), 8 81 | T.eq fizzbuzz(9), :Fizz 82 | T.eq fizzbuzz(10), :Buzz 83 | T.eq fizzbuzz(11), 11 84 | T.eq fizzbuzz(12), :Fizz 85 | T.eq fizzbuzz(13), 13 86 | T.eq fizzbuzz(14), 14 87 | T.eq fizzbuzz(15), :FizzBuzz 88 | 89 | T.eq fizzbuzz(100), :Buzz 90 | T.eq fizzbuzz(100), :FizzBuzz 91 | T.eq fizzbuzz(111), :Fizz 92 | T.eq fizzbuzz(222), :Fizz 93 | T.eq fizzbuzz(333), :Fizz 94 | T.eq fizzbuzz(444), :Fizz 95 | T.eq fizzbuzz(555), :FizzBuzz 96 | T.eq fizzbuzz(1515), :Fizz 97 | T.eq fizzbuzz(1515), :Buzz 98 | T.eq fizzbuzz(1515), :FizzBuzz 99 | end 100 | ``` 101 | 102 | then run it: 103 | 104 | ```console 105 | $ env CRYSTAL_TEST=yes crystal run fizzbuzz.cr 106 | TAP Version 13 107 | # fizzbuzz 108 | ok 1 - should equal 109 | ok 2 - should equal 110 | ok 3 - should equal 111 | ok 4 - should equal 112 | ok 5 - should equal 113 | ok 6 - should equal 114 | ok 7 - should equal 115 | ok 8 - should equal 116 | ok 9 - should equal 117 | ok 10 - should equal 118 | ok 11 - should equal 119 | ok 12 - should equal 120 | ok 13 - should equal 121 | ok 14 - should equal 122 | ok 15 - should equal 123 | ok 16 - should equal 124 | not ok 17 - should equal 125 | --- 126 | test: :- 127 | (:FizzBuzz) == fizzbuzz(100) 128 | operator: eq 129 | expected: :- 130 | :FizzBuzz 131 | actual: :- 132 | :Buzz 133 | at: :- 134 | fizzbuzz.cr - line at 28 135 | ... 136 | ok 18 - should equal 137 | ok 19 - should equal 138 | ok 20 - should equal 139 | ok 21 - should equal 140 | ok 22 - should equal 141 | not ok 23 - should equal 142 | --- 143 | test: :- 144 | (:Fizz) == fizzbuzz(1515) 145 | operator: eq 146 | expected: :- 147 | :Fizz 148 | actual: :- 149 | :FizzBuzz 150 | at: :- 151 | fizzbuzz.cr - line at 34 152 | ... 153 | not ok 24 - should equal 154 | --- 155 | test: :- 156 | (:Buzz) == fizzbuzz(1515) 157 | operator: eq 158 | expected: :- 159 | :Buzz 160 | actual: :- 161 | :FizzBuzz 162 | at: :- 163 | fizzbuzz.cr - line at 35 164 | ... 165 | ok 25 - should equal 166 | 167 | 1..25 168 | # tests 25 169 | # pass 22 170 | # fail 3 171 | ``` 172 | 173 | ## Development 174 | 175 | ```console 176 | $ crystal make.cr -- spec 177 | ``` 178 | 179 | 180 | ## Contributing 181 | 182 | 1. Fork it ( https://github.com/MakeNowJust/tap/fork ) 183 | 2. Create your feature branch (git checkout -b my-new-feature) 184 | 3. Commit your changes (git commit -am 'Add some feature') 185 | 4. Push to the branch (git push origin my-new-feature) 186 | 5. Create a new Pull Request 187 | 188 | 189 | ## Contributors 190 | 191 | - [MakeNowJust](https://github.com/MakeNowJust) TSUYUSATO Kitsune - creator, maintainer 192 | -------------------------------------------------------------------------------- /spec/expected.txt: -------------------------------------------------------------------------------- 1 | TAP Version 13 2 | ok 1 - 3 | ok 2 - one is one 4 | not ok 3 - 5 | --- 6 | test: 1 == 2 7 | operator: ok 8 | expected: true 9 | actual: false 10 | at: :- 11 | ./spec/test.cr - line at 5 12 | ... 13 | not ok 4 - one is two 14 | --- 15 | test: 1 == 2 16 | operator: ok 17 | expected: true 18 | actual: false 19 | at: :- 20 | ./spec/test.cr - line at 6 21 | ... 22 | not ok 5 - 23 | --- 24 | test: !(1 == 1) 25 | operator: not_ok 26 | expected: false 27 | actual: true 28 | at: :- 29 | ./spec/test.cr - line at 8 30 | ... 31 | not ok 6 - one is one 32 | --- 33 | test: !(1 == 1) 34 | operator: not_ok 35 | expected: false 36 | actual: true 37 | at: :- 38 | ./spec/test.cr - line at 9 39 | ... 40 | ok 7 - 41 | ok 8 - one is two 42 | ok 9 - should equal 43 | ok 10 - one is one 44 | not ok 11 - should equal 45 | --- 46 | test: 2 == 1 47 | operator: eq 48 | expected: 2 49 | actual: 1 50 | at: :- 51 | ./spec/test.cr - line at 15 52 | ... 53 | not ok 12 - one is two 54 | --- 55 | test: 2 == 1 56 | operator: eq 57 | expected: 2 58 | actual: 1 59 | at: :- 60 | ./spec/test.cr - line at 16 61 | ... 62 | not ok 13 - should not equal 63 | --- 64 | test: 1 != 1 65 | operator: not_eq 66 | expected: 1 67 | actual: 1 68 | at: :- 69 | ./spec/test.cr - line at 18 70 | ... 71 | not ok 14 - one is one 72 | --- 73 | test: 1 != 1 74 | operator: not_eq 75 | expected: 1 76 | actual: 1 77 | at: :- 78 | ./spec/test.cr - line at 19 79 | ... 80 | ok 15 - should not equal 81 | ok 16 - one is two 82 | ok 17 - should be nil 83 | not ok 18 - should be nil 84 | --- 85 | test: :- 86 | nil == (:not_nil) 87 | operator: eq 88 | expected: nil 89 | actual: :- 90 | :not_nil 91 | at: :- 92 | ./spec/test.cr - line at 24 93 | ... 94 | not ok 19 - should not be nil 95 | --- 96 | test: nil != nil 97 | operator: not_eq 98 | expected: nil 99 | actual: nil 100 | at: :- 101 | ./spec/test.cr - line at 26 102 | ... 103 | ok 20 - should not be nil 104 | ok 21 - should be true 105 | not ok 22 - should be true 106 | --- 107 | test: true == "true" 108 | operator: eq 109 | expected: true 110 | actual: "true" 111 | at: :- 112 | ./spec/test.cr - line at 30 113 | ... 114 | ok 23 - should be false 115 | not ok 24 - should be false 116 | --- 117 | test: false == "false" 118 | operator: eq 119 | expected: false 120 | actual: "false" 121 | at: :- 122 | ./spec/test.cr - line at 33 123 | ... 124 | ok 25 - should be same 125 | ok 26 - one is one 126 | not ok 27 - should be same 127 | --- 128 | test: :- 129 | cheat_one.same?(cheat_two) 130 | operator: same 131 | expected_object_id: 2 132 | expected: Cheat.new(2) 133 | actual_object_id: 1 134 | actual: Cheat.new(1) 135 | at: :- 136 | ./spec/test.cr - line at 50 137 | ... 138 | not ok 28 - one is two 139 | --- 140 | test: :- 141 | cheat_one.same?(cheat_two) 142 | operator: same 143 | expected_object_id: 2 144 | expected: Cheat.new(2) 145 | actual_object_id: 1 146 | actual: Cheat.new(1) 147 | at: :- 148 | ./spec/test.cr - line at 51 149 | ... 150 | not ok 29 - should not be same 151 | --- 152 | test: :- 153 | !(cheat_one.same?(cheat_one)) 154 | operator: not_same 155 | expected_object_id: 1 156 | expected: Cheat.new(1) 157 | actual_object_id: 1 158 | actual: Cheat.new(1) 159 | at: :- 160 | ./spec/test.cr - line at 53 161 | ... 162 | not ok 30 - one is one 163 | --- 164 | test: :- 165 | !(cheat_one.same?(cheat_one)) 166 | operator: not_same 167 | expected_object_id: 1 168 | expected: Cheat.new(1) 169 | actual_object_id: 1 170 | actual: Cheat.new(1) 171 | at: :- 172 | ./spec/test.cr - line at 54 173 | ... 174 | ok 31 - should not be same 175 | ok 32 - one is two 176 | ok 33 - should be instance of 177 | not ok 34 - should be instance of 178 | --- 179 | test: :- 180 | cheat_one.is_a?(String) 181 | operator: is 182 | expected_class: String 183 | actual: Cheat.new(1) 184 | actual_class: Cheat 185 | at: :- 186 | ./spec/test.cr - line at 59 187 | ... 188 | ok 35 - cheat object 189 | not ok 36 - cheat object 190 | --- 191 | test: :- 192 | cheat_two.is_a?(String) 193 | operator: is 194 | expected_class: String 195 | actual: Cheat.new(2) 196 | actual_class: Cheat 197 | at: :- 198 | ./spec/test.cr - line at 61 199 | ... 200 | ok 37 - should be instance of 201 | ok 38 - should be instance of 202 | not ok 39 - should be instance of 203 | --- 204 | test: :- 205 | "cheat".is_a?(Cheat) 206 | operator: is 207 | expected_class: Cheat 208 | actual: "cheat" 209 | actual_class: String 210 | at: :- 211 | ./spec/test.cr - line at 64 212 | ... 213 | ok 40 - should be instance of 214 | not ok 41 - string object 215 | --- 216 | test: :- 217 | "cheat".is_a?(Cheat) 218 | operator: is 219 | expected_class: Cheat 220 | actual: "cheat" 221 | actual_class: String 222 | at: :- 223 | ./spec/test.cr - line at 66 224 | ... 225 | ok 42 - string object 226 | not ok 43 - should not be instance of 227 | --- 228 | test: :- 229 | !(cheat_one.is_a?(Cheat)) 230 | operator: is_not 231 | expected_class: Cheat 232 | actual: Cheat.new(1) 233 | actual_class: Cheat 234 | at: :- 235 | ./spec/test.cr - line at 69 236 | ... 237 | ok 44 - should not be instance of 238 | not ok 45 - cheat object 239 | --- 240 | test: :- 241 | !(cheat_two.is_a?(Cheat)) 242 | operator: is_not 243 | expected_class: Cheat 244 | actual: Cheat.new(2) 245 | actual_class: Cheat 246 | at: :- 247 | ./spec/test.cr - line at 71 248 | ... 249 | ok 46 - cheat object 250 | not ok 47 - should not be instance of 251 | --- 252 | test: :- 253 | !([cheat_one, cheat_two].is_a?(Array(Cheat))) 254 | operator: is_not 255 | expected_class: Array(Cheat) 256 | actual: [Cheat.new(1), Cheat.new(2)] 257 | actual_class: Array(Cheat) 258 | at: :- 259 | ./spec/test.cr - line at 73 260 | ... 261 | not ok 48 - should not be instance of 262 | --- 263 | test: :- 264 | !([cheat_one, cheat_two].is_a?(Array)) 265 | operator: is_not 266 | expected_class: Array(T) 267 | actual: [Cheat.new(1), Cheat.new(2)] 268 | actual_class: Array(Cheat) 269 | at: :- 270 | ./spec/test.cr - line at 74 271 | ... 272 | ok 49 - should not be instance of 273 | not ok 50 - should not be instance of 274 | --- 275 | test: :- 276 | !("cheat".is_a?(String)) 277 | operator: is_not 278 | expected_class: String 279 | actual: "cheat" 280 | actual_class: String 281 | at: :- 282 | ./spec/test.cr - line at 76 283 | ... 284 | ok 51 - string object 285 | not ok 52 - string object 286 | --- 287 | test: :- 288 | !("cheat".is_a?(String)) 289 | operator: is_not 290 | expected_class: String 291 | actual: "cheat" 292 | actual_class: String 293 | at: :- 294 | ./spec/test.cr - line at 78 295 | ... 296 | ok 53 - should match 297 | not ok 54 - should match 298 | --- 299 | test: "Hello" === "hello" 300 | operator: match 301 | expected: "Hello" 302 | actual: "hello" 303 | at: :- 304 | ./spec/test.cr - line at 81 305 | ... 306 | ok 55 - should match 307 | ok 56 - should match 308 | ok 57 - match regex 309 | ok 58 - should match 310 | not ok 59 - should match 311 | --- 312 | test: 1..(3 === 5) 313 | operator: match 314 | expected: 1..3 315 | actual: 5 316 | at: :- 317 | ./spec/test.cr - line at 86 318 | ... 319 | not ok 60 - should not match 320 | --- 321 | test: !("hello" === "hello") 322 | operator: not_match 323 | expected: "hello" 324 | actual: "hello" 325 | at: :- 326 | ./spec/test.cr - line at 88 327 | ... 328 | ok 61 - should not match 329 | not ok 62 - should not match 330 | --- 331 | test: !((/hello/) === "hello") 332 | operator: not_match 333 | expected: /hello/ 334 | actual: "hello" 335 | at: :- 336 | ./spec/test.cr - line at 90 337 | ... 338 | not ok 63 - should not match 339 | --- 340 | test: !((/hello/i) === "Hello") 341 | operator: not_match 342 | expected: /hello/i 343 | actual: "Hello" 344 | at: :- 345 | ./spec/test.cr - line at 91 346 | ... 347 | not ok 64 - match regex 348 | --- 349 | test: !((/hello/i) === "Hello") 350 | operator: not_match 351 | expected: /hello/i 352 | actual: "Hello" 353 | at: :- 354 | ./spec/test.cr - line at 92 355 | ... 356 | not ok 65 - should not match 357 | --- 358 | test: !(1..(3 === 1)) 359 | operator: not_match 360 | expected: 1..3 361 | actual: 1 362 | at: :- 363 | ./spec/test.cr - line at 93 364 | ... 365 | ok 66 - should not match 366 | ok 67 - should raise 367 | ok 68 - raise an exception 368 | not ok 69 - should raise 369 | --- 370 | operator: raises 371 | expected: Exception 372 | actual: nil 373 | at: :- 374 | ./spec/test.cr - line at 110 375 | ... 376 | not ok 70 - not raise an exception 377 | --- 378 | operator: raises 379 | expected: Exception 380 | actual: nil 381 | at: :- 382 | ./spec/test.cr - line at 111 383 | ... 384 | ok 71 - raise an exception 385 | not ok 72 - raise an exception 386 | --- 387 | operator: raises 388 | expected: "exception" 389 | actual: :- 390 | Ex.new("error") 391 | no backtrace... 392 | at: :- 393 | ./spec/test.cr - line at 113 394 | ... 395 | ok 73 - raise an exception 396 | not ok 74 - raise an exception 397 | --- 398 | operator: raises 399 | expected: /exception/ 400 | actual: :- 401 | Ex.new("error") 402 | no backtrace... 403 | at: :- 404 | ./spec/test.cr - line at 115 405 | ... 406 | not ok 75 - should not raise 407 | --- 408 | operator: not_raises 409 | error: :- 410 | Ex.new("error") 411 | no backtrace... 412 | at: :- 413 | ./spec/test.cr - line at 117 414 | ... 415 | not ok 76 - raise an exception 416 | --- 417 | operator: not_raises 418 | error: :- 419 | Ex.new("error") 420 | no backtrace... 421 | at: :- 422 | ./spec/test.cr - line at 118 423 | ... 424 | ok 77 - should not raise 425 | ok 78 - not raise an exception 426 | # 427 | ok 79 - in unnamed group 428 | not ok 80 - in unnamed group 429 | --- 430 | test: false 431 | operator: ok 432 | expected: true 433 | actual: false 434 | at: :- 435 | ./spec/test.cr - line at 124 436 | ... 437 | # named 438 | ok 81 - in named group 439 | not ok 82 - in named group 440 | --- 441 | test: false 442 | operator: ok 443 | expected: true 444 | actual: false 445 | at: :- 446 | ./spec/test.cr - line at 129 447 | ... 448 | # catch an exception in test group 449 | ok 83 - 450 | not ok 84 - 451 | --- 452 | test: false 453 | operator: ok 454 | expected: true 455 | actual: false 456 | at: :- 457 | ./spec/test.cr - line at 134 458 | ... 459 | not ok 85 - catch an exception in test group 460 | --- 461 | error: :- 462 | Ex.new("error") 463 | no backtrace... 464 | ... 465 | ok 86 - # skip catch an exception in test group 466 | 467 | 1..86 468 | # tests 86 469 | # pass 43 470 | # fail 43 471 | -------------------------------------------------------------------------------- /src/tap/t.cr: -------------------------------------------------------------------------------- 1 | require "./output" 2 | require "./util" 3 | 4 | module T 5 | extend self 6 | 7 | @@count = 0 8 | @@pass_count = 0 9 | @@fail_count = 0 10 | 11 | @@output = Tap::Output.new STDOUT 12 | 13 | @@in_group = nil 14 | 15 | def begin 16 | @@output.version 17 | end 18 | 19 | # Shows test results and returns exit code. 20 | def end 21 | @@output.count @@count 22 | 23 | @@output.comment "tests #{@@count}" 24 | @@output.comment "pass #{@@pass_count}" 25 | @@output.comment "fail #{@@fail_count}" if @@fail_count != 0 26 | 27 | @@fail_count == 0 ? 0 : 1 28 | end 29 | 30 | # Passes a test with *message*. 31 | def pass(message) 32 | unless @@in_group == nil || @@in_group == true 33 | raise "Don't assert out of test group" 34 | end 35 | 36 | @@count += 1 37 | @@pass_count += 1 38 | @@output.result true, @@count, message.to_s 39 | end 40 | 41 | # Fails a test with *message* and *info*. 42 | def fail(message, info = nil) 43 | unless @@in_group == nil || @@in_group == true 44 | raise "Don't assert out of test group" 45 | end 46 | 47 | @@count += 1 48 | @@fail_count += 1 49 | @@output.result false, @@count, message.to_s 50 | @@output.info info if info 51 | end 52 | 53 | # Defines new test group. 54 | def test(name = "") 55 | @@output.comment name.to_s 56 | @@in_group = true 57 | begin 58 | yield 59 | rescue ex 60 | ::T.fail name, [ 61 | {"error", ex.inspect_with_backtrace}, 62 | ] 63 | end 64 | @@in_group = false 65 | end 66 | 67 | # Skips a test group. 68 | def skip(name = "", &block) 69 | @@count += 1 70 | @@pass_count += 1 71 | @@in_group = false 72 | @@output.result true, @@count, "# skip #{name}" 73 | end 74 | 75 | # Checks *test* is truthy. 76 | macro ok(test, message = nil, file = __FILE__, line = __LINE__) 77 | %test = {{ test }} 78 | if %test 79 | ::T.pass {{ message }} 80 | else 81 | ::T.fail {{ message }}, [ 82 | {"test", ::Tap::Util.expr_to_s {{ test }}}, 83 | {"operator", "ok"}, 84 | {"expected", true.inspect}, 85 | {"actual", %test.inspect}, 86 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 87 | ] 88 | end 89 | end 90 | 91 | # Checks *test* is falsely. 92 | macro not_ok(test, message = nil, file = __FILE__, line = __LINE__) 93 | %test = {{ test }} 94 | if !%test 95 | ::T.pass {{ message }} 96 | else 97 | ::T.fail {{ message }}, [ 98 | {"test", ::Tap::Util.expr_to_s !({{ test }})}, 99 | {"operator", "not_ok"}, 100 | {"expected", false.inspect}, 101 | {"actual", %test.inspect}, 102 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 103 | ] 104 | end 105 | end 106 | 107 | # Checks *actual* equals to *expected*. (`expected == actual`) 108 | macro eq(actual, expected, message = "should equal", file = __FILE__, line = __LINE__) 109 | %actual = {{ actual }} 110 | %expected = {{ expected }} 111 | if %expected == %actual 112 | ::T.pass {{ message }} 113 | else 114 | ::T.fail {{ message }}, [ 115 | {"test" , ::Tap::Util.expr_to_s {{ expected }} == {{ actual }}}, 116 | {"operator", "eq"}, 117 | {"expected", %expected.inspect}, 118 | {"actual" , %actual.inspect}, 119 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 120 | ] 121 | end 122 | end 123 | 124 | # Checks *actual* does not equal to *expected*. (`expected != actual`) 125 | macro not_eq(actual, expected, message = "should not equal", file = __FILE__, line = __LINE__) 126 | %actual = {{ actual }} 127 | %expected = {{ expected }} 128 | if %expected != %actual 129 | ::T.pass {{ message }} 130 | else 131 | ::T.fail {{ message }}, [ 132 | {"test" , ::Tap::Util.expr_to_s {{ expected }} != {{ actual }}}, 133 | {"operator", "not_eq"}, 134 | {"expected", %expected.inspect}, 135 | {"actual" , %actual.inspect}, 136 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 137 | ] 138 | end 139 | end 140 | 141 | # Checks *actual* is `nil`. (`nil == actual`) 142 | macro eq_nil(actual, message = "should be nil", file = __FILE__, line = __LINE__) 143 | ::T.eq {{ actual }}, nil, {{ message }}, {{ file }}, {{ line }} 144 | end 145 | 146 | # Checks *actual* is not `nil`. (`nil != actual`) 147 | macro not_eq_nil(actual, message = "should not be nil", file = __FILE__, line = __LINE__) 148 | ::T.not_eq {{ actual }}, nil, {{ message }}, {{ file }}, {{ line }} 149 | end 150 | 151 | # Checks *actual* is `true`. (`true == actual`) 152 | macro eq_true(actual, message = "should be true", file = __FILE__, line = __LINE__) 153 | ::T.eq {{ actual }}, true, {{ message }}, {{ file }}, {{ line }} 154 | end 155 | 156 | # Checks *actual* is `false`. (`false == actual`) 157 | macro eq_false(actual, message = "should be false", file = __FILE__, line = __LINE__) 158 | ::T.eq {{ actual}}, false, {{ message }}, {{ file }}, {{ line }} 159 | end 160 | 161 | # Checks *actual* and *expected* are same instance. (`actual.same? expected`) 162 | macro same(actual, expected, message = "should be same", file = __FILE__, line = __LINE__) 163 | %actual = {{ actual }} 164 | %expected = {{ expected }} 165 | if %actual.same? %expected 166 | ::T.pass {{ message }} 167 | else 168 | ::T.fail {{ message }}, [ 169 | {"test", ::Tap::Util.expr_to_s {{ actual }}.same?({{ expected }})}, 170 | {"operator", "same"}, 171 | {"expected_object_id", %expected.object_id.inspect}, 172 | {"expected", %expected.inspect}, 173 | {"actual_object_id", %actual.object_id.inspect}, 174 | {"actual", %actual.inspect}, 175 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 176 | ] 177 | end 178 | end 179 | 180 | # Checks *actual* and *expected* are not same instance. (`!actual.same? expected`) 181 | macro not_same(actual, expected, message = "should not be same", file = __FILE__, line = __LINE__) 182 | %actual = {{ actual }} 183 | %expected = {{ expected }} 184 | if !%actual.same? %expected 185 | ::T.pass {{ message }} 186 | else 187 | ::T.fail {{ message }}, [ 188 | {"test", ::Tap::Util.expr_to_s !({{ actual }}.same?({{ expected }}))}, 189 | {"operator", "not_same"}, 190 | {"expected_object_id", %expected.object_id.inspect}, 191 | {"expected", %expected.inspect}, 192 | {"actual_object_id", %actual.object_id.inspect}, 193 | {"actual", %actual.inspect}, 194 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 195 | ] 196 | end 197 | end 198 | 199 | # Checks *actual* is instance of *expected_class*. (`actual.is_a? expected_class`) 200 | macro is(actual, expected_class, message = "should be instance of", file = __FILE__, line = __LINE__) 201 | %actual = {{ actual }} 202 | if %actual.is_a?({{ expected_class }}) 203 | ::T.pass {{ message }} 204 | else 205 | ::T.fail {{ message }}, [ 206 | {"test", ::Tap::Util.expr_to_s {{ actual }}.is_a?({{ expected_class }})}, 207 | {"operator", "is"}, 208 | {"expected_class", {{ expected_class }}.to_s}, 209 | {"actual", %actual.inspect}, 210 | {"actual_class", %actual.class.to_s}, 211 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 212 | ] 213 | end 214 | end 215 | 216 | # Checks *actual* is not instance of *expected_class*. (`!actual.is_a? expected_class`) 217 | macro is_not(actual, expected_class, message = "should not be instance of", file = __FILE__, line = __LINE__) 218 | %actual = {{ actual }} 219 | if !%actual.is_a?({{ expected_class }}) 220 | ::T.pass {{ message }} 221 | else 222 | ::T.fail {{ message }}, [ 223 | {"test", ::Tap::Util.expr_to_s !({{ actual }}.is_a?({{ expected_class }}))}, 224 | {"operator", "is_not"}, 225 | {"expected_class", {{ expected_class }}.to_s}, 226 | {"actual", %actual.inspect}, 227 | {"actual_class", %actual.class.to_s}, 228 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 229 | ] 230 | end 231 | end 232 | 233 | # Checks *actual* matches with *expected*. (`expected === actual`) 234 | macro match(actual, expected, message = "should match", file = __FILE__, line = __LINE__) 235 | %actual = {{ actual }} 236 | %expected = {{ expected }} 237 | if %expected === %actual 238 | ::T.pass {{ message }} 239 | else 240 | ::T.fail {{ message }}, [ 241 | {"test", ::Tap::Util.expr_to_s {{ expected }} === {{ actual }}}, 242 | {"operator", "match"}, 243 | {"expected", %expected.inspect}, 244 | {"actual", %actual.inspect}, 245 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 246 | ] 247 | end 248 | end 249 | 250 | # Checks *actual* does not match with *expected*. (`!(expected === actual)`) 251 | macro not_match(actual, expected, message = "should not match", file = __FILE__, line = __LINE__) 252 | %actual = {{ actual }} 253 | %expected = {{ expected }} 254 | if !(%expected === %actual) 255 | ::T.pass {{ message }} 256 | else 257 | ::T.fail {{ message }}, [ 258 | {"test", ::Tap::Util.expr_to_s !({{ expected }} === {{ actual }})}, 259 | {"operator", "not_match"}, 260 | {"expected", %expected.inspect}, 261 | {"actual", %actual.inspect}, 262 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 263 | ] 264 | end 265 | end 266 | 267 | # Checks given block raises the *expected* exception. 268 | macro raises(expected = Exception, message = "should raise", file = __FILE__, line = __LINE__) 269 | begin 270 | {{ yield }} 271 | %actual = nil 272 | rescue %error 273 | %actual = %error 274 | end 275 | 276 | %expected = {{ expected }} 277 | if %expected === %actual || %actual.is_a?(Exception) && %expected === %actual.message 278 | ::T.pass {{ message }} 279 | else 280 | ::T.fail {{ message }}, [ 281 | {"operator", "raises"}, 282 | {"expected", {{ expected }}.inspect}, 283 | {"actual", %actual.is_a?(Exception) ? %actual.inspect_with_backtrace : %actual.inspect}, 284 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 285 | ] 286 | end 287 | end 288 | 289 | # Checks given block does not raise an exception. 290 | macro not_raises(message = "should not raise", file = __FILE__, line = __LINE__) 291 | begin 292 | {{ yield }} 293 | ::T.pass {{ message }} 294 | rescue %actual 295 | ::T.fail {{ message }}, [ 296 | {"operator", "not_raises"}, 297 | {"error", %actual.inspect_with_backtrace}, 298 | {"at", "#{ ::Tap::Util.fix_path {{ file }} } - line at #{ {{ line }} }"}, 299 | ] 300 | end 301 | end 302 | end 303 | --------------------------------------------------------------------------------