totally-awesome-tests 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a9def027932d8353e3fee64c8eff9abd74c5d485
4
+ data.tar.gz: 33a7b447b9b847d0ae3da6d24fd257b216887891
5
+ SHA512:
6
+ metadata.gz: 74420050ea6ded62946cfa13bd56f7d382e7cf689d29018cebf47ee2fe96dce51f71b1ffb6b0c3bd5f8908e77065d85e7b527161f868f11e9ec3b08f07cd9938
7
+ data.tar.gz: d57d4b5c084fb63a2ed63f3ddd5d1eeb9501b90c415d2e54403acb080a0c3e3881893051c418315ec811b6967bdc1e09854438c14a42240e059d889b8e8d8794
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in totally-awesome-tests.gemspec
4
+ gemspec
@@ -0,0 +1,35 @@
1
+ # Totess
2
+
3
+ Like \*Test but totally awesome.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'totally-awesome-tests'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install totally-awesome-tests
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it ( https://github.com/MadRabbit/totally-awesome-tests/fork )
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "totally-awesome-tests"
5
+
6
+ Totes::Cli.new ARGV.dup
@@ -0,0 +1,2 @@
1
+ # rubygem hook
2
+ require_relative "./totes"
@@ -0,0 +1,18 @@
1
+ module Totes
2
+ dir = File.dirname(__FILE__)
3
+
4
+ autoload :VERSION, "#{dir}/totes/version"
5
+ autoload :Cli, "#{dir}/totes/cli"
6
+ autoload :Resolver, "#{dir}/totes/resolver"
7
+ autoload :Runner, "#{dir}/totes/runner"
8
+ autoload :Spec, "#{dir}/totes/spec"
9
+ autoload :Noop, "#{dir}/totes/noop"
10
+ autoload :Subject, "#{dir}/totes/subject"
11
+ autoload :Matcher, "#{dir}/totes/matcher"
12
+ autoload :Query, "#{dir}/totes/query"
13
+ autoload :Error, "#{dir}/totes/error"
14
+ autoload :Reporter, "#{dir}/totes/reporter"
15
+ autoload :Backtrace, "#{dir}/totes/backtrace"
16
+ end
17
+
18
+ require_relative "./totes/kernel"
@@ -0,0 +1,5 @@
1
+ #
2
+ # The standard autorun hook
3
+ #
4
+
5
+ at_exit { Totes::Runner.start }
@@ -0,0 +1,14 @@
1
+ module Totes
2
+ module Backtrace
3
+ #
4
+ # Filters the backtrace on an error
5
+ #
6
+ def self.filter(error)
7
+ error.set_backtrace error.backtrace.map { |line|
8
+ unless line =~ /totally-awesome-tests\/lib\/totes\/[a-z_]+\.rb/
9
+ line.gsub! /:in `block.+?$/, ""
10
+ end
11
+ }.compact
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module Totes
2
+ class Cli
3
+ def initialize(args)
4
+ sources = parse_args(args)
5
+
6
+ require_relative "./autorun"
7
+ Totes::Resolver.new(sources).require_all
8
+ end
9
+
10
+ private
11
+
12
+ def parse_args(args)
13
+ [].tap do |sources|
14
+ while arg = args.shift
15
+ case arg
16
+ when '-h', '--help' then print_help
17
+ else sources << arg
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def print_help
24
+ print_nicely_and_exit %Q{
25
+ Totally awesome tests usage:
26
+
27
+ totes [path ...] [OPTIONS]
28
+
29
+ OPTIONS:
30
+ -h, --help # show this help
31
+ }
32
+ end
33
+
34
+ def print_nicely_and_exit(string)
35
+ print string.gsub(string[/\A(\n +)/], "\n").strip + "\n\n"
36
+ exit
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module Totes
2
+ class Error < StandardError
3
+
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Kernel
2
+ # the top level `describe` blocks
3
+ def describe(*args, &block)
4
+ Totes::Runner.specs << Totes::Spec.new(*args, &block)
5
+ end
6
+ end
@@ -0,0 +1,75 @@
1
+ module Totes
2
+ class Matcher
3
+ dir = File.dirname(__FILE__)
4
+
5
+ autoload :Be, "#{dir}/matcher/be"
6
+ autoload :BeA, "#{dir}/matcher/be_a"
7
+ autoload :Eql, "#{dir}/matcher/eql"
8
+ autoload :RespondTo, "#{dir}/matcher/respond_to"
9
+ autoload :BeAnything, "#{dir}/matcher/be_anything"
10
+
11
+ ALIASES = {
12
+ eq: "eql",
13
+ be_an: "be_a",
14
+ :== => "eql"
15
+ }
16
+
17
+ def self.build(name, *args, &block)
18
+ name = ALIASES[name.to_sym] || name.to_s
19
+ klass = name.gsub(/(\A|_)([a-z])/){ $2.upcase }
20
+ klass = const_get(klass) rescue nil
21
+
22
+ if !klass && name =~ /^be_[a-z]/
23
+ klass = BeAnything
24
+ args.unshift name.gsub(/^be_/, "")
25
+ end
26
+
27
+ klass && klass.new(*args, &block)
28
+ end
29
+
30
+ def self.error_must(template=nil)
31
+ template ? (@error_must = template) : @error_must
32
+ end
33
+
34
+ def self.error_wont(template=nil)
35
+ template ? (@error_wont = template) : @error_wont
36
+ end
37
+
38
+ def initialize(value)
39
+ @value = value
40
+ end
41
+
42
+ def test(subject, fail_on: false)
43
+ if likes(subject) == fail_on
44
+ fail(subject, positive: !fail_on)
45
+ end
46
+ end
47
+
48
+ def likes(subject)
49
+ raise "#{self.class.name}#likes(subject) is not implemented yet"
50
+ end
51
+
52
+ private
53
+
54
+ def fail(subject, positive: true)
55
+ super Totes::Error, build_error(subject, positive)
56
+ end
57
+
58
+ def build_error(subject, positive)
59
+ template = positive ? self.class.error_must : self.class.error_wont
60
+ template % {subject: subject.inspect}.merge(find_params_in(template))
61
+ end
62
+
63
+ def find_params_in(template)
64
+ {}.tap do |params|
65
+ template.scan(/%\{(.+?)\}/).each do |match|
66
+ var_name = match[0].to_sym
67
+
68
+ if instance_variable_defined?("@#{var_name}")
69
+ params[var_name] = instance_variable_get("@#{var_name}").inspect
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Matcher::Be < Totes::Matcher
3
+ error_must "expected %{subject} to be the same as %{value}"
4
+ error_wont "expected %{subject} not to be the same as %{value}"
5
+
6
+ def likes(subject)
7
+ subject === @value
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Matcher::BeA < Totes::Matcher
3
+ error_must "expected %{subject} to be an instance of %{value}"
4
+ error_wont "expected %{subject} not to be an instance of %{value}"
5
+
6
+ def likes(subject)
7
+ subject.is_a?(@value)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Totes
2
+ class Totes::Matcher::BeAnything < Totes::Matcher
3
+ error_must "expected %{subject}#%{method} to be true, instead have %{result}"
4
+ error_wont "expected %{subject}#%{method} to be false, instead have %{result}"
5
+
6
+ def initialize(method_name, *args, &block)
7
+ @method = method_name =~ /\?$/ ? method_name : "#{method_name}?"
8
+ @args = args
9
+ @block = block
10
+ end
11
+
12
+ def likes(subject)
13
+ @result = subject.__send__ @method, *@args, &@block
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Totes
2
+ class Matcher::Eql < Totes::Matcher
3
+ error_must "expected %{subject} to be equal %{value}"
4
+ error_wont "expected %{subject} not to be equal %{value}"
5
+
6
+ def likes(subject)
7
+ # TODO make a deep matching thing work
8
+ subject == @value
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Totes
2
+ class Totes::Matcher::RespondTo < Totes::Matcher
3
+ error_must "expected %{subject} to have method '%{value}', but it ain't have one"
4
+ error_wont "expected %{subject} not to have method '%{value}', but it got one"
5
+
6
+ def likes(subject)
7
+ subject.respond_to?(@value)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ #
2
+ # This is a completely stripped down base class
3
+ #
4
+ module Totes
5
+ class Noop
6
+ Class.new.instance_methods.each do |core_method|
7
+ define_method core_method do |*args, &block|
8
+ method_missing core_method, *args, &block
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,55 @@
1
+ #
2
+ # Represents the subject query (the left side of the assertions)
3
+ #
4
+ module Totes
5
+ class Query < Noop
6
+ def initialize(subject)
7
+ @subject = Totes::Subject.new(subject)
8
+ end
9
+
10
+ def must(*args, &block)
11
+ if matcher = args[0]
12
+ __try__ { matcher.test(@subject, fail_on: false) }
13
+ else
14
+ OperationsWrapper.new(self, :must)
15
+ end
16
+ end
17
+
18
+ def wont(*args, &block)
19
+ if matcher = args[0]
20
+ __try__ { matcher.test(@subject, fail_on: true) }
21
+ else
22
+ OperationsWrapper.new(self, :wont)
23
+ end
24
+ end
25
+
26
+ def method_missing(name, *args, &block)
27
+ Totes::Query.new(@subject.__send__ name, *args, &block)
28
+ end
29
+
30
+ private
31
+
32
+ def __try__(&block)
33
+ yield
34
+
35
+ Totes::Reporter.inst.passed self
36
+ rescue Totes::Error => e
37
+ Totes::Backtrace.filter e
38
+ Totes::Reporter.inst.failed e
39
+ end
40
+
41
+ # the naughty operations matchers wrapper
42
+ class OperationsWrapper
43
+ def initialize(query, method)
44
+ @query = query
45
+ @method = method
46
+ end
47
+
48
+ [:==, :"!=", :=~, :<=, :>=, :<, :>].each do |operation|
49
+ define_method operation do |*args, &block|
50
+ @query.__send__ @method, Totes::Matcher.build(operation, *args, &block)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ module Totes
2
+ class Reporter
3
+ def self.inst
4
+ @inst ||= new
5
+ end
6
+
7
+ def reset
8
+ @tests = 0
9
+ @fails = []
10
+ @time = Time.new
11
+ puts "Testing\n"
12
+ end
13
+
14
+ def passed(query)
15
+ @tests += 1
16
+ print "\e[32m.\e[0m"
17
+ end
18
+
19
+ def failed(error)
20
+ @tests += 1
21
+ @fails << error
22
+ print "\e[31mF\e[0m"
23
+ end
24
+
25
+ def summary
26
+ seconds = Time.new - @time
27
+ prefix = @fails.size == 0 ? "\e[32m✔︎ " : "\e[31m✖︎ "
28
+
29
+ puts "\n\n#{prefix}Tests: %d, Failed: %d \e[0m\e[;2m(%.1f seconds, %.1f tests/sec)\e[0m\n" % [
30
+ @tests, @fails.size, seconds, @tests / seconds
31
+ ]
32
+
33
+ print_failures
34
+ end
35
+
36
+ private
37
+
38
+ def print_failures
39
+ @fails.each do |error|
40
+ puts "\n\e[31mERROR: #{error.message}\e[0m"
41
+ error.backtrace.each do |line|
42
+ puts " \e[;2m#{line.gsub Dir.pwd+"/", ""}\e[0m"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,41 @@
1
+ # Finds all the test files and requires all everything
2
+ module Totes
3
+ class Resolver
4
+ def initialize(sources)
5
+ @sources = sources
6
+ end
7
+
8
+ def require_all
9
+ add_to_load_path('lib', 'test')
10
+ require_spec_helpers
11
+
12
+ Dir[*clean_dirs_list].each do |filename|
13
+ require "#{Dir.pwd}/#{filename}"
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def add_to_load_path(*dirs)
20
+ dirs.each do |dir|
21
+ if File.exists?(dir) && File.directory?(dir)
22
+ $LOAD_PATH << "#{Dir.pwd}/#{dir}/"
23
+ end
24
+ end
25
+ end
26
+
27
+ def require_spec_helpers
28
+ test_helper_file = File.join(Dir.pwd, "test/test_helper.rb")
29
+ require test_helper_file if File.exists?(test_helper_file)
30
+ end
31
+
32
+ def clean_dirs_list
33
+ @sources = ['test'] if @sources.empty?
34
+
35
+ @sources.map do |name|
36
+ name = name.slice(0, name.size - 1) if name[name.size-1] == '/'
37
+ File.exists?(name) && File.directory?(name) ? "#{name}/**/*_test.rb" : name
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ module Totes
2
+ class Runner
3
+ def self.specs
4
+ @specs ||= []
5
+ end
6
+
7
+ def self.start
8
+ new(specs).run
9
+ end
10
+
11
+ def initialize(specs)
12
+ @specs = specs
13
+ Totes::Reporter.inst.reset
14
+ end
15
+
16
+ def run
17
+ @specs.each { |spec| loop_through spec }
18
+ Totes::Reporter.inst.summary
19
+ rescue StandardError => e
20
+ Totes::Backtrace.filter e
21
+ raise e
22
+ end
23
+
24
+ private
25
+
26
+ def loop_through(spec)
27
+ exec [spec]
28
+ rescue Totes::Spec::StartOver => e
29
+ spec.instance_eval { @__count[:queries] = 0; @__specs = [] } # resetting the state
30
+ loop_through(spec)
31
+ end
32
+
33
+ def exec(specs)
34
+ specs.each do |spec|
35
+ spec.instance_eval &spec.instance_variable_get("@__block")
36
+ exec spec.instance_variable_get("@__specs")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ module Totes
2
+ class Spec
3
+ attr_reader :subject
4
+
5
+ StartOver = Class.new(StandardError)
6
+
7
+ def initialize(*args, &block)
8
+ @subject = __get_subject__(*args)
9
+ @__block = block
10
+ @__specs = [] # inner specs list
11
+ @__count = {
12
+ queries: 0, # global queries counter
13
+ current: 0 # the current query marker
14
+ }
15
+ end
16
+
17
+ def describe(*args, &block)
18
+ @__specs << self.class.new(*args, &block).tap do |spec|
19
+ spec.instance_variable_set("@__count", @__count)
20
+ end
21
+ end
22
+
23
+ # returns the _subject_ query object
24
+ def it
25
+ if @__count[:current] == @__count[:queries]
26
+ @__count[:queries] += 1
27
+ Totes::Query.new(@subject) # going in for real
28
+ elsif @__count[:current] > @__count[:queries]
29
+ @__count[:queries] += 1
30
+ SkipQuery.new # rerunning through a previous query, skipping
31
+ else
32
+ @__count[:current] += 1 # switching to the next query
33
+ raise StartOver
34
+ end
35
+ end
36
+
37
+ alias :its :it # more readable on subqueries
38
+
39
+ # builds the matchers
40
+ def method_missing(*args, &block)
41
+ Totes::Matcher.build(*args, &block) || super(*args, &block)
42
+ end
43
+
44
+ private
45
+
46
+ def __get_subject__(*args)
47
+ return args[0] # TODO make it smarter
48
+ end
49
+
50
+ class SkipQuery
51
+ def must(*args); end # don't trigger the matchers
52
+ def wont(*args); end # don't trigger the matchers
53
+
54
+ def method_missing(*args, &block)
55
+ self # endlessly return self
56
+ end
57
+
58
+ # passing core methods (like :to_s and so on onto the subject as well)
59
+ (Class.new.instance_methods - [:__send__, :__id__, :object_id]).each do |core_method|
60
+ define_method core_method do |*args, &block|
61
+ method_missing core_method, *args, &block
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # This is a smarty pants wrapper around the subject values
3
+ #
4
+ module Totes
5
+ class Subject < Noop
6
+ def initialize(subject)
7
+ @subject = subject
8
+ end
9
+
10
+ def method_missing(name, *args, &block)
11
+ # puts "#{name.inspect}(#{args.map(&:inspect).join(",")})"
12
+ @subject.__send__ name, *args, &block
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Totes
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'totes/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "totally-awesome-tests"
8
+ spec.version = Totes::VERSION
9
+ spec.authors = ["Nikolay Nemshilov"]
10
+ spec.email = ["nemshilov@gmail.com"]
11
+
12
+ spec.summary = %q{Like totally awesome testing framework}
13
+ spec.description = %q{Like totally awesome testing framework. Did I mention "totally" already?}
14
+ spec.homepage = "https://github.com/MadRabbit/totally-awesome-tests"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: totally-awesome-tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikolay Nemshilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ description: Like totally awesome testing framework. Did I mention "totally" already?
28
+ email:
29
+ - nemshilov@gmail.com
30
+ executables:
31
+ - totes
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".travis.yml"
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - bin/totes
41
+ - lib/totally-awesome-tests.rb
42
+ - lib/totes.rb
43
+ - lib/totes/autorun.rb
44
+ - lib/totes/backtrace.rb
45
+ - lib/totes/cli.rb
46
+ - lib/totes/error.rb
47
+ - lib/totes/kernel.rb
48
+ - lib/totes/matcher.rb
49
+ - lib/totes/matcher/be.rb
50
+ - lib/totes/matcher/be_a.rb
51
+ - lib/totes/matcher/be_anything.rb
52
+ - lib/totes/matcher/eql.rb
53
+ - lib/totes/matcher/respond_to.rb
54
+ - lib/totes/noop.rb
55
+ - lib/totes/query.rb
56
+ - lib/totes/reporter.rb
57
+ - lib/totes/resolver.rb
58
+ - lib/totes/runner.rb
59
+ - lib/totes/spec.rb
60
+ - lib/totes/subject.rb
61
+ - lib/totes/version.rb
62
+ - totally-awesome-tests.gemspec
63
+ homepage: https://github.com/MadRabbit/totally-awesome-tests
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.4.5
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Like totally awesome testing framework
87
+ test_files: []