├── .gitignore ├── .travis.yml ├── README.md ├── example └── sample.rb ├── mrbgem.rake ├── mrblib └── mtest_unit.rb ├── run_test.rb └── test └── mtest_unit_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # mrbgems 2 | *.tmp 3 | *.ctmp 4 | *.rbtmp 5 | *.o 6 | *.a 7 | gem_mixlib.c 8 | gem_mrblib.c 9 | gem_srclib.c 10 | .*.swp 11 | /tmp 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: 2 | - "ruby run_test.rb all test" 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Minimum Test Framework for mruby 2 | ========= 3 | 4 | [![Build Status](https://travis-ci.org/iij/mruby-mtest.svg?branch=master)](https://travis-ci.org/iij/mruby-mtest) 5 | 6 | ## example 7 | 8 | ```ruby 9 | class Test4MTest < MTest::Unit::TestCase 10 | def test_assert 11 | assert(true) 12 | assert(true, 'true sample test') 13 | end 14 | end 15 | 16 | MTest::Unit.new.run 17 | ``` 18 | 19 | This example outputs results in a format similar to minitest. 20 | 21 | You can also use `MTest::Unit.new.run` to get mruby-test style output. It is 22 | OK to use minitest style output with mrbtest. mruby-test will be notified of 23 | test runs with either method. 24 | 25 | ### How to use mrbgem's mrbtest 26 | 27 | ```ruby 28 | if Object.const_defined?(:MTest) 29 | class Test4MTest < MTest::Unit::TestCase 30 | def test_assert_nil 31 | assert_nil(nil, 'nil sample test') 32 | end 33 | end 34 | 35 | if $ok_test 36 | MTest::Unit.new.mrbtest 37 | else 38 | MTest::Unit.new.run 39 | end 40 | else 41 | $asserts << "test skip of Test4MTest." if $asserts 42 | end 43 | ``` 44 | 45 | ## TODO 46 | 47 | - MiniTest::Unit.autorun is not implemented (because mruby hasn't ``at_exit`` method.) 48 | 49 | 50 | ## License 51 | 52 | Copyright (c) 2013 Internet Initiative Japan Inc. 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a 55 | copy of this software and associated documentation files (the "Software"), 56 | to deal in the Software without restriction, including without limitation 57 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 58 | and/or sell copies of the Software, and to permit persons to whom the 59 | Software is furnished to do so, subject to the following conditions: 60 | 61 | The above copyright notice and this permission notice shall be included in 62 | all copies or substantial portions of the Software. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 69 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 70 | DEALINGS IN THE SOFTWARE. 71 | 72 | -------------------------------------------------------------------------------- /example/sample.rb: -------------------------------------------------------------------------------- 1 | class Test4MTest < MTest::Unit::TestCase 2 | def test_assert 3 | assert(true) 4 | assert(true, 'true sample test') 5 | end 6 | 7 | def test_assert_block 8 | assert_block('msg') do 9 | 'something-block' 10 | end 11 | end 12 | 13 | def test_assert_empty 14 | assert_empty('', 'string empty') 15 | assert_empty([], 'array empty') 16 | assert_empty({}, 'hash empty') 17 | end 18 | 19 | def test_assert_equal 20 | assert_equal('', nil.to_s) 21 | end 22 | 23 | def test_assert_in_delta 24 | assert_in_delta(0, 0.1, 0.5) 25 | end 26 | 27 | def test_assert_includes 28 | assert_include([1,2,3], 1) 29 | end 30 | 31 | def test_assert_instance_of 32 | assert_instance_of Array, [] 33 | assert_instance_of Class, Array 34 | end 35 | 36 | def test_assert_kind_of 37 | assert_kind_of Array, [] 38 | assert_kind_of Class, Array 39 | end 40 | 41 | def test_assert_match 42 | assert_match 'abc', 'abc' 43 | end 44 | 45 | def test_skip 46 | skip 'explain why we skip the test' 47 | end 48 | 49 | def test_failure 50 | assert(1 == 0, 'assertion to fail') 51 | end 52 | 53 | def test_error 54 | 1 + "a" 55 | end 56 | end 57 | 58 | MTest::Unit.new.run 59 | -------------------------------------------------------------------------------- /mrbgem.rake: -------------------------------------------------------------------------------- 1 | MRuby::Gem::Specification.new('mruby-mtest') do |spec| 2 | spec.license = 'MIT' 3 | spec.authors = 'Internet Initiative Japan Inc.' 4 | 5 | spec.add_dependency 'mruby-sprintf', core: 'mruby-sprintf' 6 | spec.add_dependency 'mruby-time', core: 'mruby-time' 7 | if Dir.exist?(File.join(MRUBY_ROOT, "mrbgems", "mruby-io")) 8 | spec.add_dependency 'mruby-io', core: 'mruby-io' 9 | else 10 | spec.add_dependency 'mruby-io', mgem: 'mruby-io' 11 | end 12 | if Dir.exist?(File.join(MRUBY_ROOT, "mrbgems", "mruby-metaprog")) 13 | spec.add_dependency 'mruby-metaprog', :core => 'mruby-metaprog' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /mrblib/mtest_unit.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ## 4 | # Minimal Test framework for mruby 5 | # 6 | module MTest 7 | 8 | 9 | ## 10 | # Assertion base class 11 | 12 | class Assertion < Exception; end 13 | 14 | ## 15 | # Assertion raised when skipping a test 16 | 17 | class Skip < Assertion; end 18 | 19 | module Assertions 20 | def mu_pp obj 21 | obj.inspect 22 | end 23 | 24 | def diff exp, act 25 | return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" 26 | end 27 | 28 | def _assertions= n 29 | @_assertions = n 30 | end 31 | 32 | def _assertions 33 | @_assertions = 0 unless @_assertions 34 | @_assertions 35 | end 36 | 37 | ## 38 | # Fails unless test is truthy. 39 | 40 | def assert test, msg = nil 41 | msg ||= "Failed assertion, no message given." 42 | self._assertions += 1 43 | unless test 44 | msg = msg.call if Proc === msg 45 | raise MTest::Assertion, msg 46 | end 47 | true 48 | end 49 | 50 | alias assert_true assert 51 | 52 | ## 53 | # Fails if test is truthy. 54 | def assert_false test, msg = nil 55 | msg = message(msg) { "Expected #{mu_pp(test)} to be false" } 56 | assert !test, msg 57 | end 58 | 59 | ## 60 | # Fails unless the block returns a true value. 61 | 62 | def assert_block msg = nil 63 | msg = message(msg) { "Expected block to return true value" } 64 | assert yield, msg 65 | end 66 | 67 | ## 68 | # Fails unless +obj+ is empty. 69 | 70 | def assert_empty obj, msg = nil 71 | msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" } 72 | assert_respond_to obj, :empty? 73 | assert obj.empty?, msg 74 | end 75 | 76 | ## 77 | # Fails +obj+ is not empty. 78 | 79 | def assert_not_empty obj, msg = nil 80 | msg = message(msg) { "Expected #{mu_pp(obj)} to be not empty" } 81 | assert_respond_to obj, :empty? 82 | assert !obj.empty?, msg 83 | end 84 | 85 | ## 86 | # Fails unless exp == act printing the difference between 87 | # the two, if possible. 88 | # 89 | # If there is no visible difference but the assertion fails, you 90 | # should suspect that your #== is buggy, or your inspect output is 91 | # missing crucial details. 92 | # 93 | # For floats use assert_in_delta. 94 | # 95 | # See also: MiniTest::Assertions.diff 96 | 97 | def assert_equal exp, act, msg = nil 98 | msg = message(msg, "") { diff exp, act } 99 | assert(exp == act, msg) 100 | end 101 | 102 | ## 103 | # Fails exp == act 104 | def assert_not_equal exp, act, msg = nil 105 | msg = message(msg) { 106 | "Expected #{mu_pp(exp)} to be not equal #{mu_pp(act)}" 107 | } 108 | assert(exp != act, msg) 109 | end 110 | 111 | ## 112 | # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+ 113 | # of each other. 114 | # 115 | # assert_in_delta Math::PI, (22.0 / 7.0), 0.01 116 | 117 | def assert_in_delta exp, act, delta = 0.001, msg = nil 118 | n = (exp - act).abs 119 | msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to be < #{delta}" } 120 | assert delta >= n, msg 121 | end 122 | 123 | ## 124 | # For comparing Floats. Fails unless +exp+ and +act+ have a relative 125 | # error less than +epsilon+. 126 | 127 | def assert_in_epsilon a, b, epsilon = 0.001, msg = nil 128 | assert_in_delta a, b, [a, b].min * epsilon, msg 129 | end 130 | 131 | ## 132 | # Fails unless +collection+ includes +obj+. 133 | 134 | def assert_include collection, obj, msg = nil 135 | msg = message(msg) { 136 | "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}" 137 | } 138 | assert_respond_to collection, :include? 139 | assert collection.include?(obj), msg 140 | end 141 | 142 | ## 143 | # Fails +collection+ includes +obj+ 144 | def assert_not_include collection, obj, msg = nil 145 | msg = message(msg) { 146 | "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}" 147 | } 148 | assert_respond_to collection, :include? 149 | assert !collection.include?(obj), msg 150 | end 151 | 152 | ## 153 | # Fails unless +obj+ is an instance of +cls+. 154 | 155 | def assert_instance_of cls, obj, msg = nil 156 | msg = message(msg) { 157 | "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}" 158 | } 159 | 160 | assert obj.instance_of?(cls), msg 161 | end 162 | 163 | ## 164 | # Fails unless +obj+ is a kind of +cls+. 165 | 166 | def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of 167 | msg = message(msg) { 168 | "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" } 169 | 170 | assert obj.kind_of?(cls), msg 171 | end 172 | 173 | ## 174 | # Fails unless +exp+ is =~ +act+. 175 | 176 | def assert_match exp, act, msg = nil 177 | if Object.const_defined?(:Regexp) 178 | msg = message(msg) { "Expected #{mu_pp(exp)} to match #{mu_pp(act)}" } 179 | assert_respond_to act, :"=~" 180 | exp = Regexp.new Regexp.escape exp if String === exp and String === act 181 | assert exp =~ act, msg 182 | else 183 | raise MTest::Skip, "assert_match is not defined, because Regexp is not impl." 184 | end 185 | end 186 | 187 | ## 188 | # Fails unless +obj+ is nil 189 | 190 | def assert_nil obj, msg = nil 191 | msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" } 192 | assert obj.nil?, msg 193 | end 194 | 195 | ## 196 | # For testing equality operators and so-forth. 197 | # 198 | # assert_operator 5, :<=, 4 199 | 200 | def assert_operator o1, op, o2, msg = nil 201 | msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" } 202 | assert o1.__send__(op, o2), msg 203 | end 204 | 205 | ## 206 | # Fails if stdout or stderr do not output the expected results. 207 | # Pass in nil if you don't care about that streams output. Pass in 208 | # "" if you require it to be silent. 209 | # 210 | # See also: #assert_silent 211 | 212 | def assert_output stdout = nil, stderr = nil 213 | out, err = capture_io do 214 | yield 215 | end 216 | 217 | x = assert_equal stdout, out, "In stdout" if stdout 218 | y = assert_equal stderr, err, "In stderr" if stderr 219 | 220 | (!stdout || x) && (!stderr || y) 221 | end 222 | 223 | ## 224 | # Fails unless the block raises one of +exp+ 225 | 226 | def assert_raise *exp 227 | msg = "#{exp.pop}\n" if String === exp.last 228 | 229 | begin 230 | yield 231 | rescue MTest::Skip => e 232 | return e if exp.include? MTest::Skip 233 | raise e 234 | rescue Exception => e 235 | excepted = exp.any? do |ex| 236 | if ex.instance_of?(Module) 237 | e.kind_of?(ex) 238 | else 239 | e.instance_of?(ex) 240 | end 241 | end 242 | 243 | assert excepted, exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not") 244 | 245 | return e 246 | end 247 | exp = exp.first if exp.size == 1 248 | flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." 249 | end 250 | 251 | ## 252 | # Fails unless +obj+ responds to +meth+. 253 | 254 | def assert_respond_to obj, meth, msg = nil 255 | msg = message(msg, '') { 256 | "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}" 257 | } 258 | assert obj.respond_to?(meth), msg 259 | end 260 | 261 | ## 262 | # Fails unless +exp+ and +act+ are #equal? 263 | 264 | def assert_same exp, act, msg = nil 265 | msg = message(msg) { 266 | data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] 267 | "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data 268 | } 269 | assert exp.equal?(act), msg 270 | end 271 | 272 | ## 273 | # +send_ary+ is a receiver, message and arguments. 274 | # 275 | # Fails unless the call returns a true value 276 | # TODO: I should prolly remove this from specs 277 | 278 | def assert_send send_ary, m = nil 279 | recv, msg, *args = send_ary 280 | m = message(m) { 281 | "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" } 282 | assert recv.__send__(msg, *args), m 283 | end 284 | 285 | ## 286 | # Fails if the block outputs anything to stderr or stdout. 287 | # 288 | # See also: #assert_output 289 | 290 | def assert_silent 291 | assert_output "", "" do 292 | yield 293 | end 294 | end 295 | 296 | ## 297 | # Fails unless the block throws +sym+ 298 | 299 | def assert_throws sym, msg = nil 300 | default = "Expected #{mu_pp(sym)} to have been thrown" 301 | caught = true 302 | catch(sym) do 303 | begin 304 | yield 305 | rescue ArgumentError => e # 1.9 exception 306 | default += ", not #{e.message.split(' ').last}" 307 | rescue NameError => e # 1.8 exception 308 | default += ", not #{e.name.inspect}" 309 | end 310 | caught = false 311 | end 312 | 313 | assert caught, message(msg) { default } 314 | end 315 | 316 | ## 317 | # Returns a proc that will output +msg+ along with the default message. 318 | 319 | def message msg = nil, ending = ".", &default 320 | Proc.new{ 321 | custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? 322 | "#{custom_message}#{default.call}#{ending}" 323 | } 324 | end 325 | 326 | ## 327 | # used for counting assertions 328 | 329 | def pass msg = nil 330 | assert true 331 | end 332 | 333 | ## 334 | # Skips the current test. Gets listed at the end of the run but 335 | # doesn't cause a failure exit code. 336 | 337 | # disable backtrace for mruby 338 | 339 | def skip msg = nil 340 | msg ||= "Skipped, no message given" 341 | raise MTest::Skip, msg 342 | end 343 | 344 | ## 345 | # Returns details for exception +e+ 346 | 347 | # disable backtrace for mruby 348 | 349 | def exception_details e, msg 350 | [ 351 | "#{msg}", 352 | "Class: <#{e.class}>", 353 | "Message: <#{e.message.inspect}>", 354 | "---Backtrace---", 355 | "#{e.backtrace.join("\n")}", 356 | "---------------", 357 | ].join "\n" 358 | end 359 | 360 | ## 361 | # Fails with +msg+ 362 | 363 | def flunk msg = nil 364 | msg ||= "Epic Fail!" 365 | assert false, msg 366 | end 367 | 368 | end 369 | 370 | class Unit 371 | attr_accessor :report, :failures, :errors, :skips 372 | attr_accessor :test_count, :assertion_count 373 | attr_accessor :start_time 374 | attr_accessor :help 375 | attr_accessor :verbose 376 | attr_writer :options 377 | 378 | def options 379 | @options ||= {} 380 | end 381 | 382 | @@out = $stdout 383 | @@runner = nil 384 | 385 | def self.output 386 | @@out 387 | end 388 | 389 | def self.output= stream 390 | @@out = stream 391 | end 392 | 393 | def self.runner= runner 394 | @@runner = runner 395 | end 396 | 397 | def self.runner 398 | @@runner = self.new unless @@runner 399 | @@runner 400 | end 401 | 402 | def output 403 | self.class.output 404 | end 405 | 406 | def puts *a 407 | output.puts(*a) 408 | end 409 | 410 | def print *a 411 | output.print(*a) 412 | end 413 | 414 | def puke klass, meth, e 415 | # dirty hack to find the actual filename and line number that the assertion failed at 416 | loc = e.backtrace.find {|l| l.include?(meth)} 417 | if loc 418 | idx = loc.rindex(':in ') 419 | loc = idx.nil? ? "#{loc}: #{e.message}" : "#{loc[0, idx]}: #{e.message} (#{e.class})" 420 | else 421 | loc = e.inspect 422 | end 423 | 424 | e = case e 425 | when MTest::Skip 426 | @skips += 1 427 | "Skipped:\n#{meth}(#{klass}) #{loc}\n" 428 | when MTest::Assertion 429 | @failures += 1 430 | "Failure:\n#{meth}(#{klass}) #{loc}\n" 431 | else 432 | @errors += 1 433 | bt = "" 434 | e.backtrace.each { |l| 435 | bt += "\t#{l}\n" 436 | break if l.include?(":in "+meth) 437 | } 438 | "Error:\n#{meth}(#{klass}): #{e.class}, #{e.message}\n#{bt}" 439 | end 440 | @report << e 441 | e[0, 1] 442 | end 443 | 444 | def initialize 445 | @report = [] 446 | @errors = @failures = @skips = 0 447 | @verbose = false 448 | end 449 | 450 | ## 451 | # Runs tests and outputs minitest-style results 452 | 453 | def run args = [] 454 | self.class.runner._run(args) 455 | end 456 | 457 | ## 458 | # Runs the tests and outputs mruby-test style results 459 | 460 | def mrbtest 461 | suites = TestCase.send "test_suites" 462 | return if suites.empty? 463 | 464 | results = _run_suites suites 465 | 466 | report.each_with_index do |msg, i| 467 | $asserts << "MTest #{i+1}) #{msg}" 468 | end 469 | 470 | TestCase.reset 471 | end 472 | 473 | def _run args = [] 474 | $ok_test ||= 0 475 | $ko_test ||= 0 476 | $kill_test ||= 0 477 | 478 | _run_tests 479 | 480 | @test_count ||= 0 481 | @test_count > 0 ? failures + errors : nil 482 | end 483 | 484 | def _run_tests 485 | suites = TestCase.send "test_suites" 486 | return if suites.empty? 487 | 488 | start = Time.now 489 | 490 | puts 491 | puts "# Running tests:" 492 | puts 493 | 494 | results = _run_suites suites 495 | 496 | t = Time.now - start 497 | 498 | puts 499 | puts 500 | puts sprintf("Finished tests in %.6fs, %.4f tests/s, %.4f assertions/s.", 501 | t, test_count / t, assertion_count / t) 502 | 503 | report.each_with_index do |msg, i| 504 | puts sprintf("\n%3d) %s", i+1, msg) 505 | end 506 | 507 | puts 508 | 509 | status 510 | end 511 | 512 | def _run_suites suites 513 | @test_count, @assertion_count = 0, 0 514 | 515 | results = suites.map { |suite| _run_suite suite } 516 | 517 | @test_count = results.map{ |r| r[0] }.inject(0) { |sum, tc| sum + tc } 518 | @assertion_count = results.map{ |r| r[1] }.inject(0) { |sum, ac| sum + ac } 519 | 520 | $ok_test += (test_count.to_i - failures.to_i - errors.to_i - skips.to_i) 521 | $ko_test += failures.to_i 522 | $kill_test += errors.to_i 523 | 524 | return results 525 | end 526 | 527 | def _run_suite suite 528 | header = "test_suite_header" 529 | puts send(header, suite) if respond_to? header 530 | 531 | assertions = suite.send("test_methods").map do |method| 532 | inst = suite.new method 533 | inst._assertions = 0 534 | 535 | print "#{suite}##{method} = " if @verbose 536 | 537 | @start_time = Time.now 538 | result = inst.run self 539 | time = Time.now - @start_time 540 | 541 | print sprintf("%.2f s = ", time) if @verbose 542 | print result 543 | puts if @verbose 544 | 545 | inst._assertions 546 | end 547 | 548 | return assertions.size, assertions.inject(0) { |sum, n| sum + n } 549 | end 550 | 551 | def status io = self.output 552 | format = "%d tests, %d assertions, %d failures, %d errors, %d skips" 553 | io.puts sprintf(format, test_count, assertion_count, failures, errors, skips) 554 | end 555 | 556 | class TestCase 557 | attr_reader :__name__ 558 | 559 | @@test_suites = {} 560 | 561 | def run runner 562 | result = "" 563 | begin 564 | @passed = nil 565 | self.setup 566 | self.run_setup_hooks 567 | self.__send__ self.__name__ 568 | result = "." unless io? 569 | @passed = true 570 | rescue Exception => e 571 | @passed = false 572 | result = runner.puke self.class, self.__name__, e 573 | ensure 574 | begin 575 | self.run_teardown_hooks 576 | self.teardown 577 | rescue Exception => e 578 | result = runner.puke self.class, self.__name__, e 579 | end 580 | end 581 | result 582 | end 583 | 584 | def initialize name = self.to_s 585 | @__name__ = name 586 | @__io__ = nil 587 | @passed = nil 588 | end 589 | 590 | def io 591 | @__io__ = true 592 | MTest::Unit.output 593 | end 594 | 595 | def io? 596 | @__io__ 597 | end 598 | 599 | def self.reset 600 | @@test_suites = {} 601 | end 602 | 603 | reset 604 | 605 | def self.inherited klass 606 | @@test_suites[klass] = true 607 | klass.reset_setup_teardown_hooks 608 | end 609 | 610 | def self.test_order 611 | :random 612 | end 613 | 614 | def self.test_suites 615 | hash = {} 616 | @@test_suites.keys.each{ |ts| hash[ts.to_s] = ts } 617 | hash.keys.sort.map{ |key| hash[key] } 618 | end 619 | 620 | def self.test_methods # :nodoc: 621 | methods = [] 622 | self.new.methods(true).each do |m| 623 | methods << m.to_s if m.to_s.index('test') == 0 624 | end 625 | 626 | case self.test_order 627 | when :random then 628 | max = methods.size 629 | # TODO: methods.sort.sort_by { rand max } 630 | methods 631 | when :alpha, :sorted then 632 | methods.sort 633 | else 634 | raise "Unknown test_order: #{self.test_order.inspect}" 635 | end 636 | end 637 | 638 | 639 | def passed? 640 | @passed 641 | end 642 | 643 | def setup; end 644 | def teardown; end 645 | def self.reset_setup_teardown_hooks 646 | @setup_hooks = [] 647 | @teardown_hooks = [] 648 | end 649 | reset_setup_teardown_hooks 650 | 651 | def self.add_setup_hook arg=nil, &block 652 | hook = arg || block 653 | @setup_hooks << hook 654 | end 655 | 656 | def self.setup_hooks # :nodoc: 657 | if superclass.respond_to? :setup_hooks then 658 | superclass.setup_hooks 659 | else 660 | [] 661 | end + @setup_hooks 662 | end 663 | 664 | def run_setup_hooks # :nodoc: 665 | self.class.setup_hooks.each do |hook| 666 | if hook.respond_to?(:arity) && hook.arity == 1 667 | hook.call(self) 668 | else 669 | hook.call 670 | end 671 | end 672 | end 673 | 674 | def self.add_teardown_hook arg=nil, &block 675 | hook = arg || block 676 | @teardown_hooks << hook 677 | end 678 | 679 | def self.teardown_hooks # :nodoc: 680 | if superclass.respond_to? :teardown_hooks then 681 | superclass.teardown_hooks 682 | else 683 | [] 684 | end + @teardown_hooks 685 | end 686 | 687 | def run_teardown_hooks # :nodoc: 688 | self.class.teardown_hooks.reverse.each do |hook| 689 | if hook.respond_to?(:arity) && hook.arity == 1 690 | hook.call(self) 691 | else 692 | hook.call 693 | end 694 | end 695 | end 696 | 697 | 698 | include MTest::Assertions 699 | end 700 | end 701 | end 702 | 703 | if __FILE__ == $0 704 | class Test4MTest < MTest::Unit::TestCase 705 | def setup 706 | puts '*setup' 707 | end 708 | 709 | def teardown 710 | puts '*teardown' 711 | end 712 | 713 | def test_sample 714 | puts '*test_sample' 715 | assert(true, 'true sample test') 716 | assert(true) 717 | end 718 | 719 | def test_skip 720 | skip 721 | end 722 | 723 | def test_failure 724 | assert(false, 'assertion to fail') 725 | end 726 | 727 | def test_error 728 | 1 + "a" 729 | end 730 | end 731 | 732 | MTest::Unit.new.run 733 | end 734 | -------------------------------------------------------------------------------- /run_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # mrbgems test runner 4 | # 5 | 6 | if __FILE__ == $0 7 | repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby' 8 | build_args = ARGV 9 | build_args = ['all', 'test'] if build_args.nil? or build_args.empty? 10 | 11 | Dir.mkdir 'tmp' unless File.exist?('tmp') 12 | unless File.exist?(dir) 13 | system "git clone #{repository} #{dir}" 14 | end 15 | 16 | exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}]) 17 | end 18 | 19 | MRuby::Build.new do |conf| 20 | toolchain :gcc 21 | conf.gembox 'default' 22 | 23 | conf.gem File.expand_path(File.dirname(__FILE__)) 24 | conf.gem :core => 'mruby-time' 25 | conf.gem :github => 'iij/mruby-io' 26 | end 27 | -------------------------------------------------------------------------------- /test/mtest_unit_test.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # Test of Minimal Test framework for mruby. 3 | # 4 | 5 | if Object.const_defined?(:MTest) 6 | class Test4MTest < MTest::Unit::TestCase 7 | def test_assert 8 | assert(true) 9 | assert(true, 'true sample test') 10 | assert_true(true) 11 | assert_false(false) 12 | assert_nil(nil) 13 | end 14 | 15 | def test_assert_block 16 | assert_block('msg') do 17 | 'something-block' 18 | end 19 | end 20 | 21 | def test_assert_empty 22 | assert_empty('', 'string empty') 23 | assert_empty([], 'array empty') 24 | assert_empty({}, 'hash empty') 25 | end 26 | 27 | def test_assert_equal 28 | assert_equal('', nil.to_s) 29 | assert_not_equal('a', nil.to_s) 30 | end 31 | 32 | def test_assert_in_delta 33 | assert_in_delta(0, 0.1, 0.5) 34 | end 35 | 36 | def test_assert_include 37 | assert_include([1,2,3], 1) 38 | end 39 | 40 | def test_assert_instance_of 41 | assert_instance_of Array, [] 42 | assert_instance_of Class, Array 43 | end 44 | 45 | def test_assert_kind_of 46 | assert_kind_of Array, [] 47 | assert_kind_of Class, Array 48 | end 49 | 50 | def test_assert_match 51 | assert_match 'abc', 'abc' 52 | end 53 | 54 | def test_assert_raise 55 | assert_raise(RuntimeError) do 56 | raise 57 | end 58 | end 59 | 60 | def test_assert_false 61 | assert_false(false) 62 | assert_false(nil) 63 | 64 | assert_raise(MTest::Assertion) do 65 | assert_false(true) 66 | end 67 | 68 | assert_raise(MTest::Assertion) do 69 | assert_false(1) 70 | end 71 | end 72 | 73 | def test_assert_true 74 | assert_true(true) 75 | assert_true(1) 76 | 77 | assert_raise(MTest::Assertion) do 78 | assert_true(false) 79 | end 80 | 81 | assert_raise(MTest::Assertion) do 82 | assert_true(nil) 83 | end 84 | end 85 | end 86 | 87 | if $ok_test 88 | MTest::Unit.new.mrbtest 89 | else 90 | MTest::Unit.new.run 91 | end 92 | else 93 | $asserts << "test skip of mruby-mtest/test/mtest_unit_test.rb" if $asserts 94 | end 95 | --------------------------------------------------------------------------------