-
-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add specs for Kernel#sprintf #537
Conversation
@eregon There are several issues marked with Looks like TravisCI integration is broken and build failed |
Build fails on AppVeyor on both 2.3 and 2.4
Specs run successfully on OS X so it's maybe an issue with Windows. |
On TravisCI some some not relevant tests failed |
require File.expand_path('../../../spec_helper', __FILE__) | ||
require File.expand_path('../../kernel/shared/sprintf', __FILE__) | ||
|
||
describe "File#printf" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's IO#printf
:
[1] pry(main)> File.new("README.md").method :printf
=> #<Method: File(IO)#printf>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon Got it
I checked owner for StringIO and have found it is IO::writable
in Ruby 2.2 and IO::generic_writable
in Ruby 2.3/2.4. It looks like an implementation detail and I wonder if I should test printf
for both IO::writable
and IO::generic_writable
StringIO.new.method(:printf).owner
=> IO::writable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting.
Those modules are definitely internal, even though they show up in ancestors
:
> StringIO.ancestors
=> [StringIO,
IO::generic_writable,
IO::generic_readable,
Enumerable,
Data,
Object,
PP::ObjectMixin,
Kernel,
BasicObject]
So I think it's best to share specs as possible and just have the specs in StringIO#printf
, since modules above are internal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make this a spec for IO#printf?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon do you propose to test only IO#printf or share the specs for ancestors too (File/StringIO)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon ping
That's a big and awesome PR! Re Travis, it looks like a Travis issue to me. |
I reproduce it constantly when restart builds. Looks like builds in other new PRs fail too What do you think about failed build on AppVeyor and issue with file and encoding on Windows? |
The same travis fail is happening on Ruby trunk too. |
I fixed Travis in 7d76b72, if you rebase there should not be |
Looks like some strange warnings of |
Re RuboCop, we should disable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great start!
That's smart, passing a lambda to it_behaves_like
, well done.
Could you take a look at my comments and address them?
core/file/printf_spec.rb
Outdated
end | ||
string = File.read(@filename) | ||
|
||
string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string
variable is not needed here.
core/kernel/printf_spec.rb
Outdated
|
||
printf(format, *args) | ||
|
||
$stdout = stdout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line should be in ensure
so that if printf
raises we still get back a normal $stdout
.
end | ||
|
||
it "returns a String in the same encoding as the format String if compatible" do | ||
string = "%s".force_encoding(Encoding::KOI8_U) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I didn't know this encoding before :)
|
||
result = format(string, argument) | ||
result.encoding.should equal(Encoding::UTF_8) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if format
is KOI8_U with non-7-bit characters and the argument UTF-8 with non-7-bit characters?
Could you add a spec for that?
Maybe also one with incompatible encodings (e.g. UTF-16BE and a UTF-8).
core/kernel/shared/sprintf.rb
Outdated
def obj.to_i; 0 end | ||
def obj.to_int; 1 end | ||
|
||
format("%b", obj).should == "1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a note, you could encode this constraint in the first spec with
obj.should_not_receive(:to_i)
.
But this if fine too.
core/kernel/shared/sprintf.rb
Outdated
format("%020A", 196).should == "0X000000000001.88P+7" | ||
end | ||
|
||
it "uses radix-1 when displays negative argument as a two's complemen" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
complement
core/kernel/shared/sprintf.rb
Outdated
describe "d" do | ||
it "converts argument as a decimal number" do | ||
format("%d", 112).should == "112" | ||
format("%d", -112).should == "-112" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be interesting to test %d
with a Bignum (> 2**64)
|
||
format("%1$*2$c", 97, 10).should == " a" | ||
format("%1$*2$p", [], 10).should == " []" | ||
format("%1$*2$s", "abc", 10).should == " abc" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimate corner-case, can we have format("%1$*2$s", "abc", -10)
?
core/kernel/shared/sprintf.rb
Outdated
# strange behavior | ||
it "does not affect G format" do | ||
format("%.10g", 12.1234).should == "12.1234" # expected "12.1234000000" | ||
format("%.10g", 123456789).should == "123456789" # expected "1.2345700000e+08" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a bug, could you report it? (I think a single issue asking multiple strange behaviors for sprintf is OK)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First case: is ok. g
does not print trailing zeroes, but there's the #
flag if you want that.
Second case: also ok. Precision is 10, the integer has 9 digits so fits without resorting to scientific notation. Use two more digits, or two less precision, to get scientific notation.
Doc says:
Convert a floating point number using exponential form
if the exponent is [...] or greater than or
equal to the precision, [...]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification.
core/kernel/shared/sprintf.rb
Outdated
end | ||
|
||
# strange behavior | ||
# expected to call to_str and only then to_s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's OK, see my comment above about to_s/to_str.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@marcandre If you have a bit of time, could you take a look at the cases @andrykonchin annotated as |
Wow, ambitious PR! |
@andrykonchin great job on doubting the results you get, btw. It's important to be on the lookout for bugs when spec'ing |
# irb(main):020:0* sprintf "Ä %s".encode('windows-1252'), "Ђ".encode('utf-8') | ||
# Encoding::CompatibilityError: incompatible character encodings: Windows-1252 and UTF-8 | ||
# expected to return string in UTF-8 encoding | ||
it "raises Encoding::CompatibilityError if cannot convert argument and format to any of encodings" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon There is another example of strange behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmh, so those encoding are compatible according to Encoding.compatible?:
> Encoding.compatible? "windows-1252", "UTF-8"
=> #<Encoding:UTF-8>
But "Ђ"
cannot be encoded in Windows-1252:
[13] pry(main)> "Ђ".encode('windows-1252')
Encoding::UndefinedConversionError: U+0402 to WINDOWS-1252 in conversion from UTF-8 to WINDOWS-1252
from (pry):12:in `encode'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon right,
Feature is
it returns a String in the argument's encoding if format encoding is more restrictive
.
In the example in comments Ruby can convert format string (in 'windows-1252') to utf-8 but doesn't do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's basically a consequence that
> "Ä %s".encode('windows-1252') << "Ђ".encode('utf-8')
Encoding::CompatibilityError: incompatible character encodings: Windows-1252 and UTF-8
I think Ruby never tries to re-encode a String, unless you use encode
explicitly.
The resulting encoding seems therefore to be the resulting encoding of << each %s
argument .
But you are right - it's responsibility of string concatenation algorithm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon maybe, but it looks like a bug to me, because here this feature works
UPD
Hm, according to this spec we should really get exception, but why we don't get it in "returns a String in the argument's encoding if format encoding is more restrictive"
spec? UTF-8 string contains not ASCII characters
But you are right - it's responsibility of string concatenation algorithm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But here the "foo %s"
is ASCII-only, so it works fine.
There is special treatment for Strings with only 7-bit characters for many String operations (in that case the Encoding usually doesn't matter, it's like if it was US-ASCII).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eregon right
@eregon done, except sharing |
@eregon ping |
We just to make the Windows CI (AppVeyor) pass :)
Probably the file should be opened with an encoding argument, since UTF-8 is not the default on Windows. |
@eregon done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrykonchin Thank you for this awesome PR!
Let's merge this! |
Related issue #68
Add specs for Kernel#sprintf and all related methods:
Not sure whether we should test ARGF.printf and IO#printf