eetee 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ Gemfile.lock
5
+ coverage/
6
+ doc/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ bundler_args: --without development
2
+ rvm:
3
+ - 1.9.3
4
+ - rbx-19mode
5
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
7
+ group(:test) do
8
+ gem 'mocha', '~> 0.12.0'
9
+
10
+ gem 'simplecov'
11
+ gem 'guard'
12
+ gem 'rb-fsevent'
13
+ gem 'growl'
14
+ end
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+
2
+ # parameters:
3
+ # output => the formatted to use
4
+ # backtrace => number of lines, nil = everything
5
+ guard 'eetee', :output => "BetterOutput", :backtrace => nil do
6
+ watch(%r{^lib/eetee/(.+)\.rb$}) { |m| "specs/unit/#{m[1]}_spec.rb" }
7
+ watch(%r{specs/.+\.rb$})
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Julien Ammous
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+
2
+ # Continuous integration ([![Build Status](https://secure.travis-ci.org/schmurfy/eetee.png)](http://travis-ci.org/schmurfy/eetee))
3
+
4
+ This gem is tested against these ruby by travis-ci.org:
5
+
6
+ - MRI 1.9.3
7
+ - rubinius (1.9 mode)
8
+
9
+ # What is this name ?
10
+
11
+ Finding an unused name is getting hard so I finally settled for E.T. because why not ?
12
+ I did not want to hve a gem with a two letters name so here is eetee !
13
+
14
+ # What is this gem ?
15
+
16
+ I used the bacon test framework for quite some time now but I have some issues with it which
17
+ are mostly unfixable without rewriting internals, this is what E.T. is !
18
+ From the outside the specs should run the same on both but the changed internals allow better
19
+ integration with guard amongst other things.
20
+
21
+ My goals were:
22
+ - as light as possible (like bacon)
23
+ - minimal set of helpers, go check rspec if you want more
24
+ - specs file should be executable as is, there is no "eetee" binary
25
+ - specs can be grouped without changing anything in them (run all the spec folder)
26
+ - keep bacon syntax as I am used to it and like it
27
+ - a set of extensions I used often (but not required by default)
28
+
29
+ # Usage
30
+
31
+ E.T. is using itself for its tests, you can look at the spec_helper.rb file and its tests,
32
+ here is a quickstart:
33
+
34
+ create a test file (I suppose you are using bundler, if not you should !):
35
+ ```ruby
36
+ require 'rubygems'
37
+ require 'bundler/setup'
38
+ require "eetee"
39
+
40
+ include EEtee
41
+
42
+ describe 'Tests' do
43
+ before do
44
+ @a = 3
45
+ end
46
+
47
+ should 'have access to instance variables' do
48
+ @a.should == 3
49
+ end
50
+
51
+ should 'wait 1s' do
52
+ sleep 1
53
+ 1.should == 1
54
+ end
55
+
56
+ should 'works 2' do
57
+ (40 + 5).should == 45
58
+ end
59
+
60
+ should 'fails' do
61
+ "toto".should == 4
62
+ end
63
+ end
64
+ ```
65
+
66
+ and to run it:
67
+ ```bash
68
+ $ ruby test.rb
69
+ ```
70
+
71
+ # Available extensions
72
+
73
+ ## guard
74
+ The guard is included inside the gem, just look at the Guardfile for the gem for the syntax (unfortunately guard init excepts the guard to live in a separate gem)
75
+
76
+ ## mocha
77
+ Allow mocha expectations to be considered as E.T. expectations.
78
+
79
+ ## rack
80
+ Boilet plate around rack-test to test rack applications.
81
+
82
+ ## time
83
+ Some time helpers:
84
+ time_block{ ... } => return execution time in milliseconds
85
+ freeze_time => Time.now will return the same time inside the block
86
+
87
+
88
+ # Setting up development environmeent
89
+
90
+ ```bash
91
+ # clone the repository and:
92
+ $ bundle
93
+ $ bundle exec guard
94
+ ```
95
+
96
+ the tests will run when a file changed, if only want to run all tests once:
97
+
98
+ ```bash
99
+ $ bundle exec rake
100
+ ```
101
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require "bundler/gem_tasks"
4
+
5
+ task :default => :test
6
+
7
+ task :test do
8
+
9
+ # do not generate coverage report under travis
10
+ unless ENV['TRAVIS']
11
+
12
+ require 'simplecov'
13
+ SimpleCov.command_name "E.T."
14
+ SimpleCov.start do
15
+ add_filter ".*_spec"
16
+ add_filter "/helpers/"
17
+ end
18
+ end
19
+
20
+ require 'eetee'
21
+
22
+ runner = EEtee::Runner.new
23
+ runner.run_pattern('specs/**/*_spec.rb')
24
+ runner.report_results()
25
+
26
+ end
27
+
data/eetee.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/eetee/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Julien Ammous"]
6
+ gem.email = ["schmurfy@gmail.com"]
7
+ gem.description = %q{Test framework inspired by Bacon}
8
+ gem.summary = %q{Another test framework}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.name = "eetee"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = EEtee::VERSION
16
+
17
+
18
+ gem.add_dependency 'term-ansicolor', '~> 1.0.7'
19
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module SimpleExtension
3
+ def run(&block)
4
+ super{ run_with_changes(&block) }
5
+ end
6
+
7
+ private
8
+ def run_with_changes(&block)
9
+ Thread.current[:eetee_simple_extension] = [1]
10
+ # puts "I did something before the test"
11
+ block.call
12
+ # puts "and after the test"
13
+ Thread.current[:eetee_simple_extension] << 2
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'eetee'
5
+
6
+ require_relative 'extension'
7
+ EEtee::Test.__send__(:include, SimpleExtension)
8
+
9
+ describe 'context' do
10
+ should 'use extension' do
11
+ true.should == true
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require "eetee"
5
+
6
+ include EEtee
7
+
8
+ EEtee.enable_focus_mode = true
9
+
10
+ describe 'Tests' do
11
+ before do
12
+ @a = 3
13
+ end
14
+
15
+ should 'have access to instance variables' do
16
+ @a.should == 3
17
+ end
18
+
19
+ should 'wait 1s' do
20
+ sleep 1
21
+ 1.should == 1
22
+ end
23
+
24
+ should 'works 2', :focus => true do
25
+ (40 + 5).should == 45
26
+ end
27
+
28
+ should 'fails' do
29
+ "toto".should == 4
30
+ end
31
+
32
+ describe 'nested context' do
33
+ should 'also have access to instance variables' do
34
+ @a.should == 3
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require "eetee"
4
+
5
+ include EEtee
6
+
7
+
8
+ shared(:valid_object) do |obj, n|
9
+ should 'receive correct number' do
10
+ n.should == 42
11
+ end
12
+
13
+ should 'respond to :to_i' do
14
+ obj.should.respond_to?(:to_i)
15
+ end
16
+
17
+ describe 'nested' do
18
+ should 'have a length higher than 2' do
19
+ obj.size.should > 2
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ describe 'Tests' do
26
+ before do
27
+ @obj = "string"
28
+ end
29
+
30
+ in_scope do
31
+ run_shared(:valid_object, @obj, 42)
32
+ end
33
+
34
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require "eetee"
5
+
6
+ include EEtee
7
+
8
+
9
+ describe 'Tests' do
10
+ before do
11
+ @a = 3
12
+ end
13
+
14
+ should 'have access to instance variables' do
15
+ @a.should == 3
16
+ end
17
+
18
+ should 'wait 1s' do
19
+ sleep 1
20
+ 1.should == 1
21
+ end
22
+
23
+ should 'works 2' do
24
+ (40 + 5).should == 45
25
+ end
26
+
27
+ should 'fails' do
28
+ "toto".should == 4
29
+ end
30
+
31
+ describe 'nested context' do
32
+ should 'also have access to instance variables' do
33
+ @a.should == 3
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,107 @@
1
+ module EEtee
2
+
3
+ module Assertions
4
+ def be_a(klass)
5
+ object_class = @object.class
6
+
7
+ invert_helper(
8
+ "expected instance of #{klass}, got #{object_class}",
9
+ "instance of #{klass} not expected"
10
+ ) do
11
+ object_class.should == klass
12
+ end
13
+
14
+ end
15
+
16
+ def raise(error_class = RuntimeError)
17
+ @object.should.be_a ::Proc
18
+
19
+ err = nil
20
+ begin
21
+ self.call()
22
+ rescue => ex
23
+ err = ex
24
+ end
25
+
26
+ invert_helper(
27
+ "expected to raise #{error_class}, got #{err.class}",
28
+ "expected not to raise #{error_class}"
29
+ ) do
30
+ err.class.should == error_class
31
+ end
32
+
33
+ err
34
+ end
35
+
36
+ def close?(target, error_margin)
37
+ invert_helper(
38
+ "expected #{target} += #{error_margin}, got #{target}",
39
+ "expected to be outside of #{target} += #{error_margin}, got #{target}"
40
+ ) do
41
+ (target-error_margin .. target+error_margin).should.include?(@object)
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ class AssertionWrapper < BasicObject
48
+
49
+ instance_methods.each do |name|
50
+ if name =~ /\?|^\W+$/
51
+ undef_method(name)
52
+ end
53
+ end
54
+
55
+ include Assertions
56
+
57
+ def initialize(object)
58
+ @object = object
59
+ @invert = false
60
+ end
61
+
62
+ def method_missing(name, *args, &block)
63
+ ::EEtee.current_test.reporter.increment_assertions()
64
+ ret = @object.__send__(name, *args, &block)
65
+
66
+ if !!ret == !!@invert
67
+ if args.empty?
68
+ msg = "#{@object.inspect}.#{name}() => #{ret}"
69
+ else
70
+ msg = "#{@object.inspect}.#{name}(#{args}) => #{ret}"
71
+ end
72
+
73
+ ::Kernel.raise AssertionFailed.new("#{@invert ? '[not] ' : ''}#{msg}")
74
+ end
75
+ end
76
+
77
+ def not
78
+ @invert = true
79
+ self
80
+ end
81
+
82
+ def be
83
+ self
84
+ end
85
+
86
+ private
87
+ ##
88
+ # invert result if needed.
89
+ def invert_helper(message, invert_message)
90
+ err = nil
91
+
92
+ begin
93
+ ret = yield
94
+ rescue AssertionFailed => ex
95
+ err = ex
96
+ end
97
+
98
+ if err || @invert
99
+ ::Kernel.raise AssertionFailed.new(@invert ? invert_message : message)
100
+ end
101
+
102
+ ret
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,67 @@
1
+ module EEtee
2
+
3
+ module SharedContextMethods
4
+ def before(&block)
5
+ instance_eval(&block)
6
+ end
7
+
8
+ def describe(description, &block)
9
+ vars = {}
10
+
11
+ instance_variables.reject{|name| name.to_s.start_with?('@_') }.each do |name|
12
+ value = instance_variable_get(name)
13
+ vars[name] = value
14
+ end
15
+
16
+ Context.new(description, @_level + 1, @_reporter, vars, @_focus_mode, &block)
17
+ end
18
+
19
+ def should(label, opts = {}, &block)
20
+ it("should #{label}", opts, &block)
21
+ end
22
+
23
+ def it(label, opts = {}, &block)
24
+ if !@_focus_mode || opts[:focus]
25
+ Test.new(label, @_reporter, &block)
26
+ end
27
+ end
28
+ end
29
+
30
+ class Context
31
+ attr_reader :description, :level
32
+
33
+ include SharedContextMethods
34
+
35
+ def description; @_description; end
36
+ def level; @_level; end
37
+
38
+ def initialize(description, level, reporter, vars = {}, focus_mode = false, &block)
39
+ vars.each do |name, value|
40
+ instance_variable_set(name, value)
41
+ end
42
+
43
+ @_focus_mode = focus_mode
44
+
45
+ @_description = description
46
+ @_level = level
47
+ @_reporter = reporter
48
+ @_reporter.around_context(self) do
49
+ run(&block)
50
+ end
51
+ end
52
+
53
+ def run(&block)
54
+ instance_eval(&block)
55
+ end
56
+
57
+ def run_shared(name, *args)
58
+ Shared.run(name, @_reporter, @_level, *args)
59
+ end
60
+
61
+ def in_scope(&block)
62
+ instance_eval(&block)
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,17 @@
1
+ module EEtee
2
+
3
+ class AssertionFailed < RuntimeError
4
+ attr_accessor :test
5
+
6
+ end
7
+
8
+ class Error < RuntimeError
9
+ attr_accessor :error, :test
10
+
11
+ def initialize(error, test)
12
+ @error = error
13
+ @test = test
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,47 @@
1
+ gem 'mocha', '~> 0.12.0'
2
+ require 'mocha_standalone'
3
+
4
+ #
5
+ # This extension ensure that mocha expectations are considered
6
+ # as bacon tests. Amongst other thing it allows to have a test
7
+ # containing only mocha expectations.
8
+ class MochaCounterWrapper
9
+ def initialize(reporter)
10
+ @reporter = reporter
11
+ end
12
+
13
+ def increment
14
+ @reporter.increment_assertions()
15
+ end
16
+ end
17
+
18
+
19
+
20
+ module MochaSpec
21
+ def self.included(klass)
22
+ klass.__send__(:include, Mocha::API)
23
+ end
24
+
25
+ def run(&block)
26
+ super{ run_with_mocha(&block) }
27
+ end
28
+
29
+ private
30
+ def run_with_mocha(&block)
31
+ begin
32
+ mocha_setup
33
+ block.call
34
+ mocha_verify(MochaCounterWrapper.new(@reporter))
35
+ rescue Mocha::ExpectationError => e
36
+ raise EEtee::AssertionFailed.new(e.message)
37
+ # raise EEte::Error.new(:failed, "#{e.message}\n#{e.backtrace[0...10].join("\n")}")
38
+ ensure
39
+ mocha_teardown
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ EEtee::Context.__send__(:include, Mocha::API)
46
+ EEtee::Test.__send__(:include, MochaSpec)
47
+
@@ -0,0 +1,78 @@
1
+
2
+ gem 'rack-test', '~> 0.6.2'
3
+ require 'rack/test'
4
+
5
+ module HTTPTest
6
+ def serve_app(app)
7
+ @app = Rack::Test::Session.new(
8
+ Rack::MockSession.new(app)
9
+ )
10
+ end
11
+
12
+ def request(method, url, opts = {})
13
+ @app.request(url, opts.merge(method: method))
14
+ @app.last_response
15
+ end
16
+
17
+ def propfind(url, properties = :all, opts = {})
18
+ namespaces = {
19
+ 'DAV:' => 'D',
20
+ 'urn:ietf:params:xml:ns:carddav' => 'C',
21
+ 'http://calendarserver.org/ns/' => 'APPLE1'
22
+ }
23
+
24
+ if properties == :all
25
+ body = "<D:allprop />"
26
+
27
+ else
28
+ properties = properties.map do |(name, ns)|
29
+ ns_short = namespaces[ns]
30
+ raise "unknown namespace: #{ns}" unless ns_short
31
+ %.<#{ns_short}:#{name}/>.
32
+ end
33
+
34
+ body = "<D:prop>#{properties.join("\n")}</D:prop>"
35
+ end
36
+
37
+
38
+ data = <<-EOS
39
+ <?xml version="1.0" encoding="UTF-8"?>
40
+ <D:propfind xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav" xmlns:APPLE1="http://calendarserver.org/ns/">
41
+ #{body}
42
+ </D:propfind>
43
+ EOS
44
+
45
+ request('PROPFIND', url, opts.merge(input: data))
46
+ end
47
+
48
+ def ensure_element_exists(response, expr, namespaces = {'D' => 'DAV:'})
49
+ ret = Nokogiri::XML(response.body)
50
+ ret.css(expr, namespaces).tap{|elements| elements.should.not.be.empty? }
51
+ rescue EEtee::AssertionFailed => err
52
+ raise EEtee::AssertionFailed.new("XML did not match: #{expr}")
53
+ end
54
+
55
+ def ensure_element_does_not_exists(response, expr, namespaces = {})
56
+ ret = Nokogiri::XML(response.body)
57
+ ret.css(expr, namespaces).should.be.empty?
58
+ rescue EEtee::AssertionFailed => err
59
+ raise EEtee::AssertionFailed.new("XML did match: #{expr}")
60
+ end
61
+
62
+ def element_content(response, expr, namespaces = {})
63
+ ret = Nokogiri::XML(response.body)
64
+ elements = ret.css(expr, namespaces)
65
+ if elements.empty?
66
+ :missing
67
+ else
68
+ children = elements.first.element_children
69
+ if children.empty?
70
+ :empty
71
+ else
72
+ children.first.text
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ EEtee::Context.__send__(:include, HTTPTest)
@@ -0,0 +1,25 @@
1
+ module EEteeTimeHelpers
2
+ def time_block
3
+ started_at = Time.now
4
+ yield
5
+ (Time.now - started_at) * 1000
6
+ end
7
+
8
+ # load the mocha extension first to enable
9
+ # this.
10
+ if defined?(MochaCounterWrapper)
11
+ ##
12
+ # Freeze the time, in this block Time.now
13
+ # will always return the same value.
14
+ def freeze_time(t = Time.now)
15
+ Time.stubs(:now).returns(t)
16
+ if block_given?
17
+ yield
18
+ Time.unstub(:now)
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ EEtee::Context.__send__(:include, EEteeTimeHelpers)