sus 0.1.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dccbd079d0154903705918ed6d572de0f4a4079d2f4d5fd352e8cef0a9694a05
4
- data.tar.gz: 400eb2d789f2ff17209f289ad2f0e0d89486cd200e127e80f304fdb2e29c87f3
3
+ metadata.gz: 8383ea04a6993d4e2c871ea7657c52489ef552ef3b8b07982eed6c9afe30b6eb
4
+ data.tar.gz: 2e256747d0c6f2372afc6663242888da0ab8895600e956ce962531aa29e514cd
5
5
  SHA512:
6
- metadata.gz: f7c3cb03088ccd432b27101ab654032c713bbecff85b18e6bdadf7315361a3be306778bbda012ee04e69f325f989ec093809b13d070f20b5ae8818e7deccdb7a
7
- data.tar.gz: b8519c677f9d0731bc5c5a7bbb0dd71ee302b9d5a68b115e1ec6feeb4baf6d841e76a0487b71a9ad0f78ae9a35e30370a88d26a937d1cb0c7d0a617ad77e123b
6
+ metadata.gz: 93e210ec09d3512186fe2e82ed5fae116e72dda4ba64ec3b050387c3b0b233b3841050bded33bdf31b532cee71144205410a50fe54c270bbe101e3c6ddcfda91
7
+ data.tar.gz: 28d48801189055201108645be27db4eace9b41a3e9dca8749eff807d9d192ae9f5889c98d683115265b1d42859774e05c4b87a80f082e4c95904ba0677752f9c
data/lib/sus/be.rb CHANGED
@@ -11,7 +11,7 @@ module Sus
11
11
 
12
12
  def call(assertions, subject)
13
13
  assertions.nested(self) do |assertions|
14
- assertions.assert(subject.public_send(*@arguments), self)
14
+ assertions.assert(subject.public_send(*@arguments), subject)
15
15
  end
16
16
  end
17
17
 
@@ -0,0 +1,53 @@
1
+
2
+ module Sus
3
+ class BeWithin
4
+ class Bounded
5
+ def initialize(range)
6
+ @range = range
7
+ end
8
+
9
+ def print(output)
10
+ output.write("be within ", :variable, @range)
11
+ end
12
+
13
+ def call(assertions, subject)
14
+ assertions.nested(self) do |assertions|
15
+ assertions.assert(@range.include?(subject), subject)
16
+ end
17
+ end
18
+ end
19
+
20
+ def initialize(tolerance)
21
+ @tolerance = tolerance
22
+ end
23
+
24
+ def of(value)
25
+ Bounded.new(Range.new(value - @tolerance, value + @tolerance))
26
+ end
27
+
28
+ def percent_of(value)
29
+ of(value * Rational(@tolerance, 100))
30
+ end
31
+
32
+ def print(output)
33
+ output.write("be within ", :variable, @tolerance)
34
+ end
35
+
36
+ def call(assertions, subject)
37
+ assertions.nested(self) do |assertions|
38
+ assertions.assert(subject < @tolerance, self)
39
+ end
40
+ end
41
+ end
42
+
43
+ class Base
44
+ def be_within(value)
45
+ case value
46
+ when Range
47
+ BeWithin::Bounded.new(value)
48
+ else
49
+ BeWithin.new(value)
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/sus/expect.rb CHANGED
@@ -30,59 +30,11 @@ module Sus
30
30
 
31
31
  return self
32
32
  end
33
-
34
- def to_throw(...)
35
- predicate = ThrowException.new(...)
36
-
37
- @assertions.nested(self, inverted: @inverted) do |assertions|
38
- predicate.call(assertions, @subject)
39
- end
40
-
41
- return self
42
- end
43
33
  end
44
34
 
45
35
  class Base
46
- def expect(subject)
47
- Expect.new(@assertions, subject)
48
- end
49
- end
50
-
51
- class ThrowException
52
- def initialize(exception_class, message = nil)
53
- @exception_class = exception_class
54
- @message = message
55
- end
56
-
57
- def call(assertions, value)
58
- assertions.nested(self) do |assertions|
59
- begin
60
- value.call
61
-
62
- # Didn't throw any exception, so the expectation failed:
63
- assertions.assert(false, self)
64
- rescue => exception
65
- # Did we throw the right kind of exception?
66
- if exception.is_a?(@exception_class)
67
- # Did it have the right message?
68
- if @message
69
- assertions.assert(@message === exception.message)
70
- else
71
- assertions.assert(true, self)
72
- end
73
- else
74
- raise
75
- end
76
- end
77
- end
78
- end
79
-
80
- def print(output)
81
- output << "throw exception " << output.style(@exception_class, :variable)
82
-
83
- if @message
84
- output << "with message " << output.style(@message, :variable)
85
- end
36
+ def expect(subject = nil, &block)
37
+ Expect.new(@assertions, subject || block)
86
38
  end
87
39
  end
88
40
  end
@@ -0,0 +1,41 @@
1
+
2
+ module Sus
3
+ class HaveDuration
4
+ def initialize(predicate)
5
+ @predicate = predicate
6
+ end
7
+
8
+ def print(output)
9
+ output.write("have duration ")
10
+ @predicate.print(output)
11
+ end
12
+
13
+ def call(assertions, subject)
14
+ assertions.nested(self) do |assertions|
15
+ duration = measure(subject)
16
+
17
+ @predicate.call(assertions, duration)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def measure(subject)
24
+ start_time = now
25
+
26
+ subject.call
27
+
28
+ return now - start_time
29
+ end
30
+
31
+ def now
32
+ ::Process.clock_gettime(Process::CLOCK_MONOTONIC)
33
+ end
34
+ end
35
+
36
+ class Base
37
+ def have_duration(...)
38
+ HaveDuration.new(...)
39
+ end
40
+ end
41
+ end
data/lib/sus/it.rb CHANGED
@@ -26,7 +26,6 @@ module Sus
26
26
  end
27
27
 
28
28
  def call(assertions)
29
- sleep rand
30
29
  assertions.nested(self, isolated: true) do |assertions|
31
30
  instance = self.new(assertions)
32
31
 
data/lib/sus/progress.rb CHANGED
@@ -78,8 +78,8 @@ module Sus
78
78
  def increment(amount = 1)
79
79
  @current += amount
80
80
 
81
- @bar.update(@current, @total, self.to_s)
82
- @lines.redraw(0)
81
+ @bar&.update(@current, @total, self.to_s)
82
+ @lines&.redraw(0)
83
83
 
84
84
  return self
85
85
  end
@@ -88,20 +88,20 @@ module Sus
88
88
  def expand(amount = 1)
89
89
  @total += amount
90
90
 
91
- @bar.update(@current, @total, self.to_s)
92
- @lines.redraw(0)
91
+ @bar&.update(@current, @total, self.to_s)
92
+ @lines&.redraw(0)
93
93
 
94
94
  return self
95
95
  end
96
96
 
97
97
  def report(index, context, state)
98
- @lines[index+1] = Output::Status.new(state, context)
98
+ @lines&.[]=(index+1, Output::Status.new(state, context))
99
99
 
100
100
  return self
101
101
  end
102
102
 
103
103
  def clear
104
- @lines.clear
104
+ @lines&.clear
105
105
  end
106
106
 
107
107
  def to_s
@@ -0,0 +1,49 @@
1
+ module Sus
2
+ class RaiseException
3
+ def initialize(exception_class = nil, message: nil)
4
+ @exception_class = exception_class
5
+ @message = message
6
+ end
7
+
8
+ def call(assertions, subject)
9
+ assertions.nested(self) do |assertions|
10
+ begin
11
+ subject.call
12
+
13
+ # Didn't throw any exception, so the expectation failed:
14
+ assertions.assert(false, self)
15
+ rescue => exception
16
+ # Did we throw the right kind of exception?
17
+ if exception.is_a?(@exception_class)
18
+ # Did it have the right message?
19
+ if @message
20
+ assertions.assert(@message === exception.message)
21
+ else
22
+ assertions.assert(true, self)
23
+ end
24
+ else
25
+ raise
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def print(output)
32
+ output.write("raise exception")
33
+
34
+ if @exception_class
35
+ output.write(" ", :variable, @exception_class, :reset)
36
+ end
37
+
38
+ if @message
39
+ output.write(" with message ", :variable, @message, :reset)
40
+ end
41
+ end
42
+ end
43
+
44
+ class Base
45
+ def raise_exception(...)
46
+ RaiseException.new(...)
47
+ end
48
+ end
49
+ end
data/lib/sus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sus
4
- VERSION = "0.1.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/sus.rb CHANGED
@@ -6,5 +6,9 @@ require_relative 'sus/assertions'
6
6
 
7
7
  require_relative 'sus/expect'
8
8
  require_relative 'sus/be'
9
+ require_relative 'sus/be_within'
10
+
11
+ require_relative 'sus/raise_exception'
12
+ require_relative 'sus/have_duration'
9
13
 
10
14
  require_relative 'sus/filter'
metadata CHANGED
@@ -1,33 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-05 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: samovar
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.0'
11
+ date: 2021-12-08 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description:
28
14
  email:
29
15
  executables:
30
16
  - sus
17
+ - sus-parallel
31
18
  extensions: []
32
19
  extra_rdoc_files: []
33
20
  files:
@@ -37,14 +24,12 @@ files:
37
24
  - lib/sus/assertions.rb
38
25
  - lib/sus/base.rb
39
26
  - lib/sus/be.rb
40
- - lib/sus/command/list.rb
41
- - lib/sus/command/run.rb
42
- - lib/sus/command/sequential.rb
43
- - lib/sus/command/top.rb
27
+ - lib/sus/be_within.rb
44
28
  - lib/sus/context.rb
45
29
  - lib/sus/describe.rb
46
30
  - lib/sus/expect.rb
47
31
  - lib/sus/filter.rb
32
+ - lib/sus/have_duration.rb
48
33
  - lib/sus/identity.rb
49
34
  - lib/sus/include_context.rb
50
35
  - lib/sus/it.rb
@@ -59,6 +44,7 @@ files:
59
44
  - lib/sus/output/text.rb
60
45
  - lib/sus/output/xterm.rb
61
46
  - lib/sus/progress.rb
47
+ - lib/sus/raise_exception.rb
62
48
  - lib/sus/registry.rb
63
49
  - lib/sus/shared.rb
64
50
  - lib/sus/version.rb
@@ -83,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
69
  - !ruby/object:Gem::Version
84
70
  version: '0'
85
71
  requirements: []
86
- rubygems_version: 3.2.32
72
+ rubygems_version: 3.3.0.dev
87
73
  signing_key:
88
74
  specification_version: 4
89
75
  summary: A fast and scalable test runner.
@@ -1,36 +0,0 @@
1
- require 'etc'
2
- require 'samovar'
3
-
4
- module Sus
5
- module Command
6
- class List < Samovar::Command
7
- self.description = "List all available tests."
8
-
9
- many :paths
10
-
11
- def prepare(registry)
12
- if paths&.any?
13
- paths.each do |path|
14
- registry.load(path)
15
- end
16
- else
17
- Dir.glob("test/**/*.rb").each do |path|
18
- registry.load(path)
19
- end
20
- end
21
- end
22
-
23
- def call
24
- registry = Sus::Registry.new
25
- output = Sus::Output.default
26
-
27
- prepare(registry)
28
-
29
- registry.each do |child|
30
- child.print(output)
31
- output.puts
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,104 +0,0 @@
1
- require 'etc'
2
- require 'samovar'
3
-
4
- module Sus
5
- module Command
6
- class Run < Samovar::Command
7
- self.description = "Run one or more tests."
8
-
9
- options do
10
- option '-c/--count <n>', "The number of threads to use for running tests.", type: Integer, default: Etc.nprocessors
11
- option '-r/--require <path>', ""
12
- end
13
-
14
- many :paths
15
-
16
- def prepare(registry)
17
- if paths&.any?
18
- paths.each do |path|
19
- registry.load(path)
20
- end
21
- else
22
- Dir.glob("test/**/*.rb").each do |path|
23
- registry.load(path)
24
- end
25
- end
26
- end
27
-
28
- Result = Struct.new(:job, :assertions)
29
-
30
- def call
31
- registry = Sus::Registry.new
32
- jobs = Thread::Queue.new
33
- results = Thread::Queue.new
34
-
35
- output = Sus::Output.default
36
- guard = Thread::Mutex.new
37
- progress = Sus::Progress.new(output)
38
- count = @options[:count]
39
-
40
- loader = Thread.new do
41
- prepare(registry)
42
-
43
- registry.each do |child|
44
- guard.synchronize{progress.expand}
45
- jobs << child
46
- end
47
-
48
- jobs.close
49
- end
50
-
51
- aggregation = Thread.new do
52
- assertions = Sus::Assertions.new(output: output.buffered)
53
- first = true
54
-
55
- while result = results.pop
56
- guard.synchronize{progress.increment}
57
-
58
- if result.assertions.failed?
59
- if first
60
- first = false
61
- else
62
- assertions.output.puts
63
- end
64
-
65
- result.assertions.output.append(assertions.output)
66
- end
67
-
68
- assertions.add(result.assertions)
69
- guard.synchronize{progress.report(count, assertions, :busy)}
70
- end
71
-
72
- guard.synchronize{progress.clear}
73
-
74
- assertions.output.puts unless first
75
- assertions.output.append(output)
76
-
77
- assertions.print(output)
78
- output.puts
79
- end
80
-
81
- workers = count.times.map do |index|
82
- Thread.new do
83
- while job = jobs.pop
84
- guard.synchronize{progress.report(index, job, :busy)}
85
-
86
- assertions = Sus::Assertions.new(output: output.buffered)
87
- job.call(assertions)
88
- results << Result.new(job, assertions)
89
-
90
- guard.synchronize{progress.report(index, "idle", :free)}
91
- end
92
- end
93
- end
94
-
95
- loader.join
96
-
97
- workers.each(&:join)
98
- results.close
99
-
100
- aggregation.join
101
- end
102
- end
103
- end
104
- end
@@ -1,35 +0,0 @@
1
- require 'etc'
2
- require 'samovar'
3
-
4
- module Sus
5
- module Command
6
- class Sequential < Samovar::Command
7
- self.description = "Run one or more tests."
8
-
9
- many :paths
10
-
11
- def prepare(registry)
12
- if paths&.any?
13
- paths.each do |path|
14
- registry.load(path)
15
- end
16
- else
17
- Dir.glob("test/**/*.rb").each do |path|
18
- registry.load(path)
19
- end
20
- end
21
- end
22
-
23
- Result = Struct.new(:job, :assertions)
24
-
25
- def call
26
- registry = Sus::Registry.new
27
- output = Sus::Output.default
28
-
29
- prepare(registry)
30
-
31
- registry.call
32
- end
33
- end
34
- end
35
- end
@@ -1,26 +0,0 @@
1
- require 'samovar'
2
- require_relative 'run'
3
- require_relative 'sequential'
4
- require_relative 'list'
5
-
6
- module Sus
7
- module Command
8
- class Top < Samovar::Command
9
- self.description = "Test your code."
10
-
11
- nested :command, {
12
- 'run' => Run,
13
- 'sequential' => Sequential,
14
- 'list' => List,
15
- }, default: 'run'
16
-
17
- def call
18
- if command = self.command
19
- command.call
20
- else
21
- self.print_usage
22
- end
23
- end
24
- end
25
- end
26
- end