handoff 0.1.0 → 0.2.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.
- data/README +3 -1
- data/lib/handoff.rb +16 -8
- data/lib/handoff/assertion.rb +17 -20
- data/lib/handoff/verifying_delegate.rb +21 -0
- data/test/example_test.rb +1 -5
- data/test/handoff/argument_normalizer_test.rb +5 -5
- data/test/handoff/assertion_test.rb +64 -55
- data/test/handoff/verifying_delegate_test.rb +36 -0
- data/test/handoff_test.rb +25 -19
- data/test/test_helper_test.rb +1 -1
- metadata +7 -13
data/README
CHANGED
@@ -3,7 +3,9 @@ stdlib module +Forwardable+ makes implementing delegation simple and readable.
|
|
3
3
|
Handoff is intended to make the code to test it just as simple and
|
4
4
|
readable.
|
5
5
|
|
6
|
-
|
6
|
+
You can install the latest gem using <code>gem install handoff</code> or download the .gem file from http://rubyforge.org/projects/handoff.
|
7
|
+
|
8
|
+
As of 0.2.0, Handoff no longer depends on Mocha and Stubba.
|
7
9
|
|
8
10
|
Here's example_test.rb to give you an idea of what you can do:
|
9
11
|
|
data/lib/handoff.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'handoff/assertion'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/handoff/assertion')
|
2
2
|
|
3
3
|
# +require+ (or +require_gem+) 'handoff' and #assert_handoff can
|
4
4
|
# be called from any Test::Unit::TestCase test method to create
|
@@ -7,14 +7,28 @@ require 'handoff/assertion'
|
|
7
7
|
# After defining Handoff, this file includes it in Test::Unit::TestCase.
|
8
8
|
module Handoff
|
9
9
|
|
10
|
+
TEARDOWN_ALIAS = :teardown_before_handoff_rewrite
|
11
|
+
|
10
12
|
# Creates an Assertion, handing a it a mock named 'handoff delegate' on which
|
11
13
|
# expectations will be set.
|
12
14
|
def assert_handoff
|
13
|
-
|
15
|
+
ensure_handoff_teardown_defined
|
16
|
+
a = Handoff::Assertion.new(self)
|
14
17
|
handoff_assertions << a
|
15
18
|
a
|
16
19
|
end
|
17
20
|
|
21
|
+
def ensure_handoff_teardown_defined
|
22
|
+
return if respond_to?(TEARDOWN_ALIAS)
|
23
|
+
class << self
|
24
|
+
alias_method TEARDOWN_ALIAS, :teardown
|
25
|
+
def teardown
|
26
|
+
conduct_and_clear_handoffs
|
27
|
+
send TEARDOWN_ALIAS
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
18
32
|
def handoff_assertions
|
19
33
|
@handoff_assertions ||= []
|
20
34
|
end
|
@@ -27,12 +41,6 @@ module Handoff
|
|
27
41
|
end
|
28
42
|
end
|
29
43
|
|
30
|
-
def self.included(base)
|
31
|
-
base.class_eval do
|
32
|
-
add_teardown_method :conduct_and_clear_handoffs
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
44
|
end
|
37
45
|
|
38
46
|
class Test::Unit::TestCase
|
data/lib/handoff/assertion.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/argument_normalizer')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/usage_error')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/verifying_delegate')
|
3
4
|
|
4
5
|
module Handoff
|
5
6
|
# This is where the fluent stuff is.
|
@@ -9,7 +10,7 @@ module Handoff
|
|
9
10
|
# * +to+,
|
10
11
|
# * +for+, +for_method+ or +for_methods+, and
|
11
12
|
# * (optionally) +with+ or +with_arguments+.
|
12
|
-
class Assertion
|
13
|
+
class Assertion < Struct.new(:test)
|
13
14
|
|
14
15
|
# :stopdoc:
|
15
16
|
|
@@ -17,10 +18,6 @@ module Handoff
|
|
17
18
|
attr_reader :delegator
|
18
19
|
private :method_map, :delegator
|
19
20
|
|
20
|
-
def initialize mock_delegate
|
21
|
-
@mock_delegate = mock_delegate
|
22
|
-
end
|
23
|
-
|
24
21
|
# :startdoc:
|
25
22
|
|
26
23
|
# Creates an instance of +class_under_test+ with no arguments and uses it
|
@@ -64,7 +61,6 @@ module Handoff
|
|
64
61
|
|
65
62
|
# Specifies that the assertion should pass no arguments to the delegating method and
|
66
63
|
# require that no arguments are sent to the target method.
|
67
|
-
# Equivalent to calling +with_arguments+ with no arguments.
|
68
64
|
def with_no_arguments
|
69
65
|
with_arguments
|
70
66
|
end
|
@@ -73,19 +69,19 @@ module Handoff
|
|
73
69
|
|
74
70
|
def conduct
|
75
71
|
validate_sufficiently_specified
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
expectation.with(*@args) unless @args.nil?
|
82
|
-
end
|
83
|
-
method_map.each_pair do |delegating_method, target|
|
84
|
-
send_args = [delegating_method, @args].flatten
|
85
|
-
actual = delegator.send(*send_args)
|
86
|
-
unless actual.equal? expected_returns[delegating_method]
|
87
|
-
raise "expected #{delegating_method.to_s} to return #{expected_returns[delegating_method]}, but was #{actual}"
|
72
|
+
method_map.each do |delegating_method, target_method|
|
73
|
+
mock_delegate = VerifyingDelegate.new(target_method, @args||[])
|
74
|
+
accessor = @delegate_accessor
|
75
|
+
(class << @delegator; self; end).class_eval do
|
76
|
+
define_method(accessor) { mock_delegate }
|
88
77
|
end
|
78
|
+
send_args = [delegating_method]
|
79
|
+
if @args
|
80
|
+
send_args << @args
|
81
|
+
send_args.flatten!
|
82
|
+
end
|
83
|
+
actual_return = @delegator.send *send_args
|
84
|
+
test.assert_equal mock_delegate.happy_return, actual_return
|
89
85
|
end
|
90
86
|
end
|
91
87
|
|
@@ -95,6 +91,7 @@ module Handoff
|
|
95
91
|
raise UsageError, "Delegate accessor must be specified with 'to'" unless @delegate_accessor
|
96
92
|
raise UsageError, "Delegating methods must be specified with 'for_method' or 'for_methods'" unless @method_map
|
97
93
|
end
|
94
|
+
|
98
95
|
# :startdoc:
|
99
96
|
end
|
100
97
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Handoff
|
2
|
+
class VerifyingDelegate
|
3
|
+
def initialize target_method, expected_args
|
4
|
+
@target_method = target_method
|
5
|
+
(class << self; self; end).class_eval do
|
6
|
+
define_method target_method do |*actual_args|
|
7
|
+
raise( ArgumentError, "expected arguments: '#{expected_args}' but got '#{actual_args}'") unless expected_args == actual_args
|
8
|
+
happy_return
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def happy_return
|
14
|
+
"return value from #{to_s}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing method, *args
|
18
|
+
raise NoMethodError, "Expected handoff to method `#{@target_method}', but `#{method}' was called"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/test/example_test.rb
CHANGED
@@ -19,12 +19,8 @@ class Foo
|
|
19
19
|
end
|
20
20
|
|
21
21
|
require 'test/unit'
|
22
|
-
require 'rubygems'
|
23
22
|
|
24
|
-
require '
|
25
|
-
require 'stubba' # ditto
|
26
|
-
|
27
|
-
require File.dirname(__FILE__) + '/../lib/handoff' # I require it this way because I don't want the installed gem.
|
23
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/handoff') # I require it this way because I don't want the installed gem.
|
28
24
|
# require 'handoff' # This is how you'd require handoff (or do require_gem w/ a version).
|
29
25
|
|
30
26
|
class ExampleTest < Test::Unit::TestCase
|
@@ -1,20 +1,20 @@
|
|
1
|
-
require '
|
2
|
-
require File.dirname(__FILE__) + '/../../lib/handoff/argument_normalizer'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/argument_normalizer')
|
3
3
|
|
4
4
|
module Handoff
|
5
5
|
class ArgumentNormalizerTest < Test::Unit::TestCase
|
6
6
|
|
7
|
-
|
7
|
+
test "should return hash at front of one element array" do
|
8
8
|
hash = {}
|
9
9
|
assert_same hash, [hash].extend(ArgumentNormalizer).to_method_map
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
test "should return self referencing hash for array of symbols" do
|
13
13
|
args = [:bar, :foo].extend(ArgumentNormalizer)
|
14
14
|
assert_equal({:foo => :foo, :bar => :bar}, args.to_method_map)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
test "should return self referencing hash for one element array with symbol" do
|
18
18
|
args = [:foo].extend(ArgumentNormalizer)
|
19
19
|
assert_equal({:foo => :foo}, args.to_method_map)
|
20
20
|
end
|
@@ -1,6 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require File.dirname(__FILE__) + '
|
3
|
-
require File.dirname(__FILE__) + '/../../lib/handoff/assertion'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/assertion')
|
4
3
|
require 'rubygems'
|
5
4
|
require 'mocha'
|
6
5
|
require 'stubba'
|
@@ -13,63 +12,73 @@ module Handoff
|
|
13
12
|
assert_same assertion, assertion.from_any(mock('class under test', :new => nil))
|
14
13
|
end
|
15
14
|
|
16
|
-
test 'should
|
17
|
-
cut = Class.new do
|
18
|
-
extend Forwardable
|
19
|
-
def_delegator :asdf, :meth
|
20
|
-
attr_reader :asdf
|
21
|
-
end
|
22
|
-
mock_delegate = mock
|
23
|
-
object_under_test = cut.new
|
24
|
-
assertion = Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
|
25
|
-
assertion.conduct
|
26
|
-
assert_equal :asdf, object_under_test.mocha.expectations.first.method_name
|
27
|
-
assert_equal :meth, mock_delegate.expectations.last.method_name
|
28
|
-
end
|
29
|
-
|
30
|
-
test 'should create mock_delegate expectation with arguments when supplied' do
|
15
|
+
test 'conduct should pass with one Test::Unit assertion if delegator does its job' do
|
31
16
|
delegator = Class.new do
|
32
|
-
|
33
|
-
|
34
|
-
attr_reader :delegate
|
17
|
+
def foo; delegate.foo; end
|
18
|
+
def delegate; nil; end
|
35
19
|
end.new
|
36
|
-
assertion = Assertion.new(
|
37
|
-
assertion.
|
20
|
+
assertion = Assertion.new(mock(:assert_equal => nil))
|
21
|
+
assertion.from(delegator).to(:delegate).for(:foo)
|
38
22
|
assertion.conduct
|
39
|
-
assert_true mock_delegate.expectations.first.match?(:meth, 'arg1', :arg2, 3)
|
40
|
-
assert_false mock_delegate.expectations.first.match?(:meth)
|
41
|
-
assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
|
42
|
-
end
|
43
|
-
|
44
|
-
{
|
45
|
-
:with => lambda {|assertion| assertion.with},
|
46
|
-
:with_no_arguments => lambda {|assertion| assertion.with_no_arguments}
|
47
|
-
}.each_pair do |method, proc|
|
48
|
-
test "should force no args when #{method} is used to specify none" do
|
49
|
-
delegator = Class.new do
|
50
|
-
extend Forwardable
|
51
|
-
def_delegator :delegate, :meth
|
52
|
-
attr_reader :delegate
|
53
|
-
end.new
|
54
|
-
assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
|
55
|
-
proc.call(assertion)
|
56
|
-
assertion.conduct
|
57
|
-
assert_true mock_delegate.expectations.first.match?(:meth)
|
58
|
-
assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
|
59
|
-
end
|
60
23
|
end
|
61
24
|
|
62
|
-
test 'should
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
25
|
+
# test 'should create expectations on mock_delegate and object under test upon conduct' do
|
26
|
+
# cut = Class.new do
|
27
|
+
# extend Forwardable
|
28
|
+
# def_delegator :asdf, :meth
|
29
|
+
# attr_reader :asdf
|
30
|
+
# end
|
31
|
+
# mock_delegate = mock
|
32
|
+
# object_under_test = cut.new
|
33
|
+
# assertion = Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
|
34
|
+
# assertion.conduct
|
35
|
+
# assert_equal :asdf, object_under_test.mocha.expectations.first.method_name
|
36
|
+
# assert_equal :meth, mock_delegate.expectations.last.method_name
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# test 'should create mock_delegate expectation with arguments when supplied' do
|
40
|
+
# delegator = Class.new do
|
41
|
+
# extend Forwardable
|
42
|
+
# def_delegator :delegate, :meth
|
43
|
+
# attr_reader :delegate
|
44
|
+
# end.new
|
45
|
+
# assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
|
46
|
+
# assertion.with('arg1', :arg2, 3)
|
47
|
+
# assertion.conduct
|
48
|
+
# assert_true mock_delegate.expectations.first.match?(:meth, 'arg1', :arg2, 3)
|
49
|
+
# assert_false mock_delegate.expectations.first.match?(:meth)
|
50
|
+
# assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# {
|
54
|
+
# :with => lambda {|assertion| assertion.with},
|
55
|
+
# :with_no_arguments => lambda {|assertion| assertion.with_no_arguments}
|
56
|
+
# }.each_pair do |method, proc|
|
57
|
+
# test "should force no args when #{method} is used to specify none" do
|
58
|
+
# delegator = Class.new do
|
59
|
+
# extend Forwardable
|
60
|
+
# def_delegator :delegate, :meth
|
61
|
+
# attr_reader :delegate
|
62
|
+
# end.new
|
63
|
+
# assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
|
64
|
+
# proc.call(assertion)
|
65
|
+
# assertion.conduct
|
66
|
+
# assert_true mock_delegate.expectations.first.match?(:meth)
|
67
|
+
# assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# test 'should allow for args or no args if nothing specified' do
|
72
|
+
# delegator = Class.new do
|
73
|
+
# extend Forwardable
|
74
|
+
# def_delegator :delegate, :meth
|
75
|
+
# attr_reader :delegate
|
76
|
+
# end.new
|
77
|
+
# assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
|
78
|
+
# assertion.conduct
|
79
|
+
# assert_true mock_delegate.expectations.first.match?(:meth)
|
80
|
+
# assert_true mock_delegate.expectations.first.match?(:meth, 'arg1')
|
81
|
+
# end
|
73
82
|
|
74
83
|
test 'validates presence of delegator, delegate accessor, and delegating methods' do
|
75
84
|
assertion = Assertion.new(mock).from(nil).to(:asdf).for_method(:meth)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/verifying_delegate')
|
3
|
+
|
4
|
+
module Handoff
|
5
|
+
class VerifyingDelegateTest < Test::Unit::TestCase
|
6
|
+
test "happy_return value" do
|
7
|
+
d = VerifyingDelegate.new(:foo, [])
|
8
|
+
assert_equal "return value from #{d.to_s}", d.happy_return
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'responds to target method with happy_return value' do
|
12
|
+
d = VerifyingDelegate.new(:foo, [])
|
13
|
+
assert_equal d.happy_return, d.foo
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'happy if args match' do
|
17
|
+
d = VerifyingDelegate.new(:foo, [1])
|
18
|
+
assert_equal d.happy_return, d.foo(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'raises if args dont match' do
|
22
|
+
d = VerifyingDelegate.new(:foo, [1])
|
23
|
+
assert_raises(ArgumentError) { d.foo }
|
24
|
+
end
|
25
|
+
|
26
|
+
test 'raises NoMethodError with helpful message if wrong method is invoked' do
|
27
|
+
d = VerifyingDelegate.new(:foo, [])
|
28
|
+
begin
|
29
|
+
d.foob
|
30
|
+
fail
|
31
|
+
rescue NoMethodError => e
|
32
|
+
assert_equal "Expected handoff to method `foo', but `foob' was called", e.message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/test/handoff_test.rb
CHANGED
@@ -1,26 +1,9 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
require 'rubygems'
|
3
3
|
require 'mocha'
|
4
|
-
require '
|
5
|
-
require File.dirname(__FILE__) + '/../lib/handoff'
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/handoff')
|
6
5
|
|
7
6
|
class HandoffTest < Test::Unit::TestCase
|
8
|
-
test 'should create a mock named "handoff delegate" and hand it to a new Assertion' do
|
9
|
-
given_name = nil
|
10
|
-
def self.mock name
|
11
|
-
assert_equal 'handoff delegate', name
|
12
|
-
:mock_return
|
13
|
-
end
|
14
|
-
Assertion.expects(:new).with(:mock_return).returns(:new_assertion)
|
15
|
-
assert_equal :new_assertion, assert_handoff
|
16
|
-
assert_equal :new_assertion, handoff_assertions.delete(:new_assertion)
|
17
|
-
assert_true handoff_assertions.empty?
|
18
|
-
end
|
19
|
-
|
20
|
-
test 'should add a teardown method for conducting handoff assertions' do
|
21
|
-
assert_true self.class.teardown_methods.include?(:conduct_and_clear_handoffs)
|
22
|
-
end
|
23
|
-
|
24
7
|
test 'should conduct each assertion in conduct_and_clear_handoffs' do
|
25
8
|
handoff_assertions << assertion1 = mock(:conduct => nil)
|
26
9
|
handoff_assertions << assertion2 = mock(:conduct => nil)
|
@@ -29,4 +12,27 @@ class HandoffTest < Test::Unit::TestCase
|
|
29
12
|
assertion1.verify
|
30
13
|
assertion2.verify
|
31
14
|
end
|
15
|
+
|
16
|
+
def blank_test_case
|
17
|
+
Class.new(Test::Unit::TestCase) do
|
18
|
+
define_method(:initialize) { super(:test_foo) }
|
19
|
+
define_method(:test_foo) {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'first call to assert_handoff in a test aliases and defines new teardown' do
|
24
|
+
test = blank_test_case.new
|
25
|
+
assert_equal [], test.singleton_methods
|
26
|
+
test.assert_handoff
|
27
|
+
assert_equal %w(teardown teardown_before_handoff_rewrite ), test.singleton_methods.sort
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'handoff-defined teardown does conduct_and_clear_handoffs then calls original teardown' do
|
31
|
+
test = blank_test_case.new
|
32
|
+
test.assert_handoff
|
33
|
+
test.expects(:conduct_and_clear_handoffs)
|
34
|
+
test.expects(:teardown_before_handoff_rewrite)
|
35
|
+
test.teardown
|
36
|
+
end
|
37
|
+
|
32
38
|
end
|
data/test/test_helper_test.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: handoff
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-05-18 00:00:00 -07:00
|
8
8
|
summary: Handoff is a fluent interface for making test assertions about delegation.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -12,7 +12,7 @@ email: ""
|
|
12
12
|
homepage: http://handoff.rubyforge.org/
|
13
13
|
rubyforge_project:
|
14
14
|
description:
|
15
|
-
autorequire:
|
15
|
+
autorequire: handoff
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
18
|
has_rdoc: true
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/handoff/argument_normalizer.rb
|
35
35
|
- lib/handoff/assertion.rb
|
36
36
|
- lib/handoff/usage_error.rb
|
37
|
+
- lib/handoff/verifying_delegate.rb
|
37
38
|
- README
|
38
39
|
test_files:
|
39
40
|
- test/example_test.rb
|
@@ -41,6 +42,7 @@ test_files:
|
|
41
42
|
- test/test_helper_test.rb
|
42
43
|
- test/handoff/argument_normalizer_test.rb
|
43
44
|
- test/handoff/assertion_test.rb
|
45
|
+
- test/handoff/verifying_delegate_test.rb
|
44
46
|
rdoc_options: []
|
45
47
|
|
46
48
|
extra_rdoc_files:
|
@@ -51,13 +53,5 @@ extensions: []
|
|
51
53
|
|
52
54
|
requirements: []
|
53
55
|
|
54
|
-
dependencies:
|
55
|
-
|
56
|
-
name: mocha
|
57
|
-
version_requirement:
|
58
|
-
version_requirements: !ruby/object:Gem::Version::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">"
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: 0.0.0
|
63
|
-
version:
|
56
|
+
dependencies: []
|
57
|
+
|