Skip to content

Commit c78696a

Browse files
committed
Add mutant-minitest integration and mutation coverage tests
1 parent b5881e4 commit c78696a

15 files changed

Lines changed: 953 additions & 43 deletions

.mutant.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
usage: opensource
2+
3+
environment_variables:
4+
MUTANT: "true"
5+
6+
integration:
7+
name: minitest
8+
9+
includes:
10+
- lib
11+
- test
12+
13+
requires:
14+
- http/form_data
15+
16+
mutation:
17+
timeout: 10.0
18+
19+
matcher:
20+
subjects:
21+
- HTTP::FormData*

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ group :test do
1414

1515
gem "simplecov", require: false
1616

17+
gem "mutant-minitest"
18+
1719
gem "yardstick"
1820
end
1921

Rakefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ Yardstick::Rake::Verify.new do |verify|
2323
verify.threshold = 100
2424
end
2525

26+
desc "Run mutation testing with Mutant"
27+
task :mutant do
28+
system("bundle exec mutant run") || abort("Mutant failed!")
29+
end
30+
2631
task default: %i[test rubocop verify_measurements]

lib/http/form_data.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ def create(data, encoder: nil)
6363
# @raise [Error] `obj` can't be coerced
6464
# @return [Hash]
6565
def ensure_hash(obj)
66-
if obj.nil? then {}
67-
elsif obj.is_a?(Hash) then obj
66+
if obj.is_a?(Hash) then obj
6867
elsif obj.respond_to?(:to_h) then obj.to_h
6968
else raise Error, "#{obj.inspect} is neither Hash nor responds to :to_h"
7069
end
@@ -95,9 +94,7 @@ def ensure_data(obj)
9594
# @return [Boolean]
9695
def multipart?(data)
9796
data.any? do |_, v|
98-
next true if v.is_a? FormData::Part
99-
100-
v.respond_to?(:to_ary) && v.to_ary.any?(FormData::Part)
97+
v.is_a?(Part) || (v.respond_to?(:to_ary) && v.to_ary.any?(Part))
10198
end
10299
end
103100
end

lib/http/form_data/composite_io.rb

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ class CompositeIO
1313
#
1414
# @api public
1515
# @param [Array<IO>] ios Array of IO objects
16-
def initialize(ios) # rubocop:disable Metrics/MethodLength
17-
@index = 0
18-
@buffer = "".b
19-
@ios = ios.map do |io|
16+
def initialize(ios)
17+
@index = 0
18+
@ios = ios.map do |io|
2019
if io.is_a?(String)
2120
StringIO.new(io)
2221
elsif io.respond_to?(:read)
@@ -76,7 +75,7 @@ def rewind
7675
#
7776
# @api private
7877
# @return [void]
79-
def read_chunks(length = nil)
78+
def read_chunks(length)
8079
while (chunk = readpartial(length))
8180
yield chunk.force_encoding(Encoding::BINARY)
8281

@@ -92,24 +91,16 @@ def read_chunks(length = nil)
9291
#
9392
# @api private
9493
# @return [String, nil]
95-
def readpartial(max_length = nil)
96-
while current_io
97-
chunk = current_io.read(max_length, @buffer)
94+
def readpartial(max_length)
95+
while (io = @ios.at(@index))
96+
chunk = io.read(max_length)
9897

9998
return chunk if chunk && !chunk.empty?
10099

101100
advance_io
102101
end
103102
end
104103

105-
# Returns IO object under the cursor
106-
#
107-
# @api private
108-
# @return [IO, nil]
109-
def current_io
110-
@ios[@index]
111-
end
112-
113104
# Advances cursor to the next IO object
114105
#
115106
# @api private

lib/http/form_data/file.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ class File < Part
4646
# @option opts [#to_s] :filename
4747
# When `path_or_io` is a String, Pathname or File, defaults to basename.
4848
# When `path_or_io` is a IO, defaults to `"stream-{object_id}"`
49-
def initialize(path_or_io, opts = {}) # rubocop:disable Lint/MissingSuper
49+
def initialize(path_or_io, opts = nil) # rubocop:disable Lint/MissingSuper
5050
opts = FormData.ensure_hash(opts)
5151

5252
if opts.key? :mime_type
5353
warn "[DEPRECATED] :mime_type option deprecated, use :content_type"
54-
opts[:content_type] = opts[:mime_type]
54+
opts[:content_type] = opts.fetch(:mime_type)
5555
end
5656

5757
@io = make_io(path_or_io)
@@ -68,8 +68,8 @@ def initialize(path_or_io, opts = {}) # rubocop:disable Lint/MissingSuper
6868
# @return [IO]
6969
def make_io(path_or_io)
7070
if path_or_io.is_a?(String)
71-
::File.open(path_or_io, binmode: true)
72-
elsif defined?(Pathname) && path_or_io.is_a?(Pathname)
71+
::File.new(path_or_io, binmode: true)
72+
elsif path_or_io.is_a?(Pathname)
7373
path_or_io.open(binmode: true)
7474
else
7575
path_or_io
@@ -83,7 +83,7 @@ def make_io(path_or_io)
8383
# @return [String]
8484
def filename_for(io)
8585
if io.respond_to?(:path)
86-
::File.basename io.path
86+
io.path.split(::File::SEPARATOR).last
8787
else
8888
"stream-#{io.object_id}"
8989
end

lib/http/form_data/multipart/param.rb

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ def initialize(name, value)
3333
@name = name.to_s
3434

3535
@part =
36-
if value.is_a?(FormData::Part)
36+
if value.is_a?(Part)
3737
value
3838
else
39-
FormData::Part.new(value)
39+
Part.new(value)
4040
end
4141

4242
@io = CompositeIO.new [header, @part, footer]
@@ -49,11 +49,10 @@ def initialize(name, value)
4949
# @api private
5050
# @return [String]
5151
def header
52-
header = "".b
53-
header << "Content-Disposition: form-data; #{parameters}#{CRLF}"
54-
header << "Content-Type: #{content_type}#{CRLF}" if content_type
55-
header << CRLF
56-
header
52+
parts = ["Content-Disposition: form-data; #{parameters}#{CRLF}"]
53+
parts << "Content-Type: #{content_type}#{CRLF}" if content_type
54+
parts << CRLF
55+
parts.join
5756
end
5857

5958
# Builds Content-Disposition parameters string
@@ -87,7 +86,7 @@ def filename
8786
# @api private
8887
# @return [String]
8988
def footer
90-
CRLF.dup
89+
"\r\n"
9190
end
9291
end
9392
end

lib/http/form_data/urlencoded.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def encoder=(implementation)
6363
# @see .encoder=
6464
# @return [#call]
6565
def encoder
66-
@encoder ||= DefaultEncoder.method(:encode)
66+
@encoder || DefaultEncoder
6767
end
6868

6969
# Default encoder for urlencoded form data
@@ -90,6 +90,8 @@ def encode(value, prefix = nil)
9090
end
9191
end
9292

93+
alias call encode
94+
9395
private
9496

9597
# Encodes an Array value

0 commit comments

Comments
 (0)