Skip to content

Instantly share code, notes, and snippets.

@shirosaki
Created February 4, 2012 03:29
Show Gist options
  • Save shirosaki/1734954 to your computer and use it in GitHub Desktop.
Save shirosaki/1734954 to your computer and use it in GitHub Desktop.
Fenix patch
With long name conversion.
Run options: --seed 40925
# Running tests:
...................................S
Finished tests in 2.681746s, 13.4241 tests/s, 22.3735 assertions/s.
36 tests, 60 assertions, 0 failures, 0 errors, 1 skips
File.expand_path: 10000 times.
Rehearsal -------------------------------------------------------------
Ruby '' 0.125000 0.312000 0.437000 ( 0.436025)
Fenix '' 0.031000 0.000000 0.031000 ( 0.027002)
Plain '' 0.016000 0.000000 0.016000 ( 0.027001)
Ruby '.' 0.156000 0.297000 0.453000 ( 0.437025)
Fenix '.' 0.016000 0.000000 0.016000 ( 0.028002)
Plain '.' 0.031000 0.000000 0.031000 ( 0.027001)
Ruby 'foo', 'bar' 0.187000 0.436000 0.623000 ( 0.624013)
Fenix 'foo', 'bar' 0.047000 0.203000 0.250000 ( 0.262401)
Plain 'foo', 'bar' 0.031000 0.000000 0.031000 ( 0.040000)
Ruby '', 'C:/' 0.016000 0.000000 0.016000 ( 0.010000)
Fenix '', 'C:/' 0.031000 0.000000 0.031000 ( 0.030000)
Plain '', 'C:/' 0.015000 0.000000 0.015000 ( 0.020000)
Ruby 'foo', 'C:/' 0.125000 0.905000 1.030000 ( 1.029022)
Fenix 'foo', 'C:/' 0.031000 0.218000 0.249000 ( 0.246014)
Plain 'foo', 'C:/' 0.032000 0.000000 0.032000 ( 0.027002)
Ruby '~' 0.249000 0.312000 0.561000 ( 0.570030)
Fenix '~' 0.031000 0.000000 0.031000 ( 0.031002)
Plain '~' 0.032000 0.000000 0.032000 ( 0.030002)
Ruby '~/foo' 0.249000 0.312000 0.561000 ( 0.549031)
Fenix '~/foo' 0.125000 0.094000 0.219000 ( 0.229012)
Plain '~/foo' 0.047000 0.000000 0.047000 ( 0.037001)
Ruby 'foo/' 0.062000 0.343000 0.405000 ( 0.416024)
Fenix 'foo/' 0.109000 0.125000 0.234000 ( 0.230013)
Plain 'foo/' 0.032000 0.000000 0.032000 ( 0.027002)
Ruby '~', 'C:/Foo' 0.171000 0.390000 0.561000 ( 0.571829)
Fenix '~', 'C:/Foo' 0.047000 0.000000 0.047000 ( 0.033001)
Plain '~', 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.037003)
Ruby long_path 0.156000 0.000000 0.156000 ( 0.155008)
Fenix long_path 0.109000 0.000000 0.109000 ( 0.114007)
Plain long_path 0.110000 0.000000 0.110000 ( 0.104006)
Ruby long_path, 'rel' 0.265000 0.296000 0.561000 ( 0.577031)
Fenix long_path, 'rel' 0.125000 0.000000 0.125000 ( 0.122007)
Plain long_path, 'rel' 0.109000 0.000000 0.109000 ( 0.109006)
Ruby long_path, 'C:/Foo' 0.218000 0.952000 1.170000 ( 1.178864)
Fenix long_path, 'C:/Foo' 0.125000 0.000000 0.125000 ( 0.118007)
Plain long_path, 'C:/Foo' 0.109000 0.000000 0.109000 ( 0.104006)
Ruby full_long_path 0.125000 0.000000 0.125000 ( 0.133007)
Fenix full_long_path 0.109000 0.000000 0.109000 ( 0.107006)
Plain full_long_path 0.094000 0.000000 0.094000 ( 0.097003)
Ruby to_path 0.047000 0.218000 0.265000 ( 0.260015)
Fenix to_path 0.140000 0.110000 0.250000 ( 0.252014)
Plain to_path 0.031000 0.000000 0.031000 ( 0.030002)
Ruby to_path, 'rel' 0.141000 0.530000 0.671000 ( 0.666435)
Fenix to_path, 'rel' 0.093000 0.156000 0.249000 ( 0.261015)
Plain to_path, 'rel' 0.031000 0.000000 0.031000 ( 0.037002)
Ruby to_path, 'C:/Foo' 0.125000 1.154000 1.279000 ( 1.284871)
Fenix to_path, 'C:/Foo' 0.093000 0.172000 0.265000 ( 0.259213)
Plain to_path, 'C:/Foo' 0.032000 0.000000 0.032000 ( 0.037601)
Ruby full_to_path 0.062000 0.936000 0.998000 ( 1.020056)
Fenix full_to_path 0.031000 0.218000 0.249000 ( 0.251015)
Plain full_to_path 0.016000 0.000000 0.016000 ( 0.022001)
--------------------------------------------------- total: 13.260000sec
user system total real
Ruby '' 0.125000 0.312000 0.437000 ( 0.434025)
Fenix '' 0.031000 0.000000 0.031000 ( 0.026001)
Plain '' 0.031000 0.000000 0.031000 ( 0.026002)
Ruby '.' 0.062000 0.375000 0.437000 ( 0.434023)
Fenix '.' 0.016000 0.000000 0.016000 ( 0.027001)
Plain '.' 0.031000 0.000000 0.031000 ( 0.026002)
Ruby 'foo', 'bar' 0.172000 0.468000 0.640000 ( 0.633036)
Fenix 'foo', 'bar' 0.093000 0.156000 0.249000 ( 0.253812)
Plain 'foo', 'bar' 0.032000 0.000000 0.032000 ( 0.033002)
Ruby '', 'C:/' 0.015000 0.000000 0.015000 ( 0.017001)
Fenix '', 'C:/' 0.031000 0.000000 0.031000 ( 0.024001)
Plain '', 'C:/' 0.032000 0.000000 0.032000 ( 0.023002)
Ruby 'foo', 'C:/' 0.093000 0.920000 1.013000 ( 1.022055)
Fenix 'foo', 'C:/' 0.063000 0.187000 0.250000 ( 0.248014)
Plain 'foo', 'C:/' 0.031000 0.000000 0.031000 ( 0.026002)
Ruby '~' 0.218000 0.344000 0.562000 ( 0.570032)
Fenix '~' 0.031000 0.000000 0.031000 ( 0.030000)
Plain '~' 0.032000 0.000000 0.032000 ( 0.033002)
Ruby '~/foo' 0.249000 0.312000 0.561000 ( 0.548031)
Fenix '~/foo' 0.031000 0.202000 0.233000 ( 0.235013)
Plain '~/foo' 0.032000 0.000000 0.032000 ( 0.031002)
Ruby 'foo/' 0.124000 0.281000 0.405000 ( 0.414221)
Fenix 'foo/' 0.063000 0.172000 0.235000 ( 0.228013)
Plain 'foo/' 0.031000 0.000000 0.031000 ( 0.027001)
Ruby '~', 'C:/Foo' 0.218000 0.343000 0.561000 ( 0.572031)
Fenix '~', 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.033002)
Plain '~', 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.036002)
Ruby long_path 0.156000 0.000000 0.156000 ( 0.152009)
Fenix long_path 0.110000 0.000000 0.110000 ( 0.112006)
Plain long_path 0.109000 0.000000 0.109000 ( 0.099006)
Ruby long_path, 'rel' 0.249000 0.312000 0.561000 ( 0.563032)
Fenix long_path, 'rel' 0.125000 0.000000 0.125000 ( 0.126006)
Plain long_path, 'rel' 0.109000 0.000000 0.109000 ( 0.107006)
Ruby long_path, 'C:/Foo' 0.172000 0.998000 1.170000 ( 1.172865)
Fenix long_path, 'C:/Foo' 0.125000 0.000000 0.125000 ( 0.115006)
Plain long_path, 'C:/Foo' 0.093000 0.000000 0.093000 ( 0.101006)
Ruby full_long_path 0.141000 0.000000 0.141000 ( 0.131008)
Fenix full_long_path 0.109000 0.000000 0.109000 ( 0.108006)
Plain full_long_path 0.094000 0.000000 0.094000 ( 0.093005)
Ruby to_path 0.031000 0.219000 0.250000 ( 0.258012)
Fenix to_path 0.110000 0.140000 0.250000 ( 0.249014)
Plain to_path 0.031000 0.000000 0.031000 ( 0.030002)
Ruby to_path, 'rel' 0.140000 0.515000 0.655000 ( 0.660036)
Fenix to_path, 'rel' 0.078000 0.187000 0.265000 ( 0.261015)
Plain to_path, 'rel' 0.031000 0.000000 0.031000 ( 0.037002)
Ruby to_path, 'C:/Foo' 0.078000 1.217000 1.295000 ( 1.285071)
Fenix to_path, 'C:/Foo' 0.110000 0.156000 0.266000 ( 0.264015)
Plain to_path, 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.031002)
Ruby full_to_path 0.062000 0.952000 1.014000 ( 1.012856)
Fenix full_to_path 0.031000 0.218000 0.249000 ( 0.250011)
Plain full_to_path 0.016000 0.000000 0.016000 ( 0.022001)
Without long name conversion.
Run options: --seed 59008
# Running tests:
...................................S
Finished tests in 0.116007s, 310.3261 tests/s, 517.2102 assertions/s.
36 tests, 60 assertions, 0 failures, 0 errors, 1 skips
File.expand_path: 10000 times.
Rehearsal -------------------------------------------------------------
Ruby '' 0.062000 0.390000 0.452000 ( 0.441023)
Fenix '' 0.016000 0.000000 0.016000 ( 0.027002)
Plain '' 0.031000 0.000000 0.031000 ( 0.027001)
Ruby '.' 0.109000 0.327000 0.436000 ( 0.440025)
Fenix '.' 0.031000 0.000000 0.031000 ( 0.027002)
Plain '.' 0.032000 0.000000 0.032000 ( 0.027002)
Ruby 'foo', 'bar' 0.062000 0.578000 0.640000 ( 0.632833)
Fenix 'foo', 'bar' 0.031000 0.000000 0.031000 ( 0.034002)
Plain 'foo', 'bar' 0.031000 0.000000 0.031000 ( 0.034002)
Ruby '', 'C:/' 0.016000 0.000000 0.016000 ( 0.017001)
Fenix '', 'C:/' 0.031000 0.000000 0.031000 ( 0.024002)
Plain '', 'C:/' 0.016000 0.000000 0.016000 ( 0.025001)
Ruby 'foo', 'C:/' 0.109000 0.920000 1.029000 ( 1.025056)
Fenix 'foo', 'C:/' 0.031000 0.000000 0.031000 ( 0.025002)
Plain 'foo', 'C:/' 0.016000 0.000000 0.016000 ( 0.027001)
Ruby '~' 0.265000 0.312000 0.577000 ( 0.568829)
Fenix '~' 0.031000 0.000000 0.031000 ( 0.031001)
Plain '~' 0.031000 0.000000 0.031000 ( 0.030002)
Ruby '~/foo' 0.297000 0.250000 0.547000 ( 0.547031)
Fenix '~/foo' 0.031000 0.000000 0.031000 ( 0.032002)
Plain '~/foo' 0.031000 0.000000 0.031000 ( 0.031002)
Ruby 'foo/' 0.156000 0.249000 0.405000 ( 0.411021)
Fenix 'foo/' 0.031000 0.000000 0.031000 ( 0.028002)
Plain 'foo/' 0.032000 0.000000 0.032000 ( 0.028002)
Ruby '~', 'C:/Foo' 0.234000 0.328000 0.562000 ( 0.570032)
Fenix '~', 'C:/Foo' 0.046000 0.000000 0.046000 ( 0.034002)
Plain '~', 'C:/Foo' 0.032000 0.000000 0.032000 ( 0.036002)
Ruby long_path 0.140000 0.015000 0.155000 ( 0.155009)
Fenix long_path 0.109000 0.000000 0.109000 ( 0.106006)
Plain long_path 0.094000 0.000000 0.094000 ( 0.103006)
Ruby long_path, 'rel' 0.249000 0.328000 0.577000 ( 0.574033)
Fenix long_path, 'rel' 0.110000 0.000000 0.110000 ( 0.113007)
Plain long_path, 'rel' 0.124000 0.000000 0.124000 ( 0.109803)
Ruby long_path, 'C:/Foo' 0.266000 0.889000 1.155000 ( 1.182464)
Fenix long_path, 'C:/Foo' 0.109000 0.000000 0.109000 ( 0.110007)
Plain long_path, 'C:/Foo' 0.109000 0.000000 0.109000 ( 0.102006)
Ruby full_long_path 0.125000 0.016000 0.141000 ( 0.134007)
Fenix full_long_path 0.093000 0.000000 0.093000 ( 0.101006)
Plain full_long_path 0.094000 0.000000 0.094000 ( 0.097006)
Ruby to_path 0.078000 0.187000 0.265000 ( 0.261015)
Fenix to_path 0.031000 0.000000 0.031000 ( 0.025000)
Plain to_path 0.031000 0.000000 0.031000 ( 0.038001)
Ruby to_path, 'rel' 0.203000 0.468000 0.671000 ( 0.663038)
Fenix to_path, 'rel' 0.031000 0.000000 0.031000 ( 0.038002)
Plain to_path, 'rel' 0.032000 0.000000 0.032000 ( 0.037002)
Ruby to_path, 'C:/Foo' 0.234000 1.061000 1.295000 ( 1.280071)
Fenix to_path, 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.031200)
Plain to_path, 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.036601)
Ruby full_to_path 0.109000 0.905000 1.014000 ( 1.017056)
Fenix full_to_path 0.031000 0.000000 0.031000 ( 0.026002)
Plain full_to_path 0.016000 0.000000 0.016000 ( 0.022001)
--------------------------------------------------- total: 11.544000sec
user system total real
Ruby '' 0.172000 0.265000 0.437000 ( 0.433025)
Fenix '' 0.031000 0.000000 0.031000 ( 0.027002)
Plain '' 0.015000 0.000000 0.015000 ( 0.025001)
Ruby '.' 0.156000 0.265000 0.421000 ( 0.433025)
Fenix '.' 0.031000 0.000000 0.031000 ( 0.025001)
Plain '.' 0.016000 0.000000 0.016000 ( 0.020000)
Ruby 'foo', 'bar' 0.156000 0.484000 0.640000 ( 0.635834)
Fenix 'foo', 'bar' 0.031000 0.000000 0.031000 ( 0.033002)
Plain 'foo', 'bar' 0.031000 0.000000 0.031000 ( 0.033002)
Ruby '', 'C:/' 0.031000 0.000000 0.031000 ( 0.017001)
Fenix '', 'C:/' 0.016000 0.000000 0.016000 ( 0.024001)
Plain '', 'C:/' 0.031000 0.000000 0.031000 ( 0.024001)
Ruby 'foo', 'C:/' 0.063000 0.951000 1.014000 ( 1.022057)
Fenix 'foo', 'C:/' 0.031000 0.000000 0.031000 ( 0.025001)
Plain 'foo', 'C:/' 0.031000 0.000000 0.031000 ( 0.025002)
Ruby '~' 0.296000 0.265000 0.561000 ( 0.565030)
Fenix '~' 0.032000 0.000000 0.032000 ( 0.031002)
Plain '~' 0.031000 0.000000 0.031000 ( 0.028001)
Ruby '~/foo' 0.265000 0.281000 0.546000 ( 0.545031)
Fenix '~/foo' 0.031000 0.000000 0.031000 ( 0.032002)
Plain '~/foo' 0.031000 0.000000 0.031000 ( 0.031002)
Ruby 'foo/' 0.110000 0.297000 0.407000 ( 0.410821)
Fenix 'foo/' 0.031000 0.000000 0.031000 ( 0.027002)
Plain 'foo/' 0.031000 0.000000 0.031000 ( 0.027001)
Ruby '~', 'C:/Foo' 0.218000 0.358000 0.576000 ( 0.571033)
Fenix '~', 'C:/Foo' 0.032000 0.000000 0.032000 ( 0.033001)
Plain '~', 'C:/Foo' 0.031000 0.000000 0.031000 ( 0.030001)
Ruby long_path 0.156000 0.000000 0.156000 ( 0.160008)
Fenix long_path 0.109000 0.000000 0.109000 ( 0.104006)
Plain long_path 0.094000 0.000000 0.094000 ( 0.099005)
Ruby long_path, 'rel' 0.234000 0.344000 0.578000 ( 0.570032)
Fenix long_path, 'rel' 0.109000 0.000000 0.109000 ( 0.111007)
Plain long_path, 'rel' 0.093000 0.000000 0.093000 ( 0.107006)
Ruby long_path, 'C:/Foo' 0.234000 0.936000 1.170000 ( 1.174064)
Fenix long_path, 'C:/Foo' 0.094000 0.000000 0.094000 ( 0.107006)
Plain long_path, 'C:/Foo' 0.109000 0.000000 0.109000 ( 0.101006)
Ruby full_long_path 0.125000 0.000000 0.125000 ( 0.132007)
Fenix full_long_path 0.094000 0.000000 0.094000 ( 0.100006)
Plain full_long_path 0.093000 0.000000 0.093000 ( 0.094006)
Ruby to_path 0.125000 0.140000 0.265000 ( 0.257014)
Fenix to_path 0.031000 0.000000 0.031000 ( 0.033000)
Plain to_path 0.032000 0.000000 0.032000 ( 0.030002)
Ruby to_path, 'rel' 0.187000 0.468000 0.655000 ( 0.657037)
Fenix to_path, 'rel' 0.031000 0.000000 0.031000 ( 0.037003)
Plain to_path, 'rel' 0.047000 0.000000 0.047000 ( 0.037002)
Ruby to_path, 'C:/Foo' 0.203000 1.061000 1.264000 ( 1.285870)
Fenix to_path, 'C:/Foo' 0.046000 0.000000 0.046000 ( 0.034002)
Plain to_path, 'C:/Foo' 0.032000 0.000000 0.032000 ( 0.031002)
Ruby full_to_path 0.062000 0.936000 0.998000 ( 1.012807)
Fenix full_to_path 0.015000 0.000000 0.015000 ( 0.024001)
Plain full_to_path 0.031000 0.000000 0.031000 ( 0.021001)
diff --git a/bench/file_expand_path.rb b/bench/file_expand_path.rb
index 47db827..c69f28a 100644
--- a/bench/file_expand_path.rb
+++ b/bench/file_expand_path.rb
@@ -17,58 +17,76 @@ puts "File.expand_path: #{TESTS} times."
Benchmark.bmbm do |results|
results.report("Ruby ''") { TESTS.times { File.expand_path('') } }
results.report("Fenix ''") { TESTS.times { Fenix::File.expand_path('') } }
+ results.report("Plain ''") { TESTS.times { Fenix::File.expand_path_plain('') } }
results.report("Ruby '.'") { TESTS.times { File.expand_path('.') } }
results.report("Fenix '.'") { TESTS.times { Fenix::File.expand_path('.') } }
+ results.report("Plain '.'") { TESTS.times { Fenix::File.expand_path_plain('.') } }
results.report("Ruby 'foo', 'bar'") { TESTS.times { File.expand_path('foo', 'bar') } }
results.report("Fenix 'foo', 'bar'") { TESTS.times { Fenix::File.expand_path('foo', 'bar') } }
+ results.report("Plain 'foo', 'bar'") { TESTS.times { Fenix::File.expand_path_plain('foo', 'bar') } }
results.report("Ruby '', 'C:/'") { TESTS.times { File.expand_path('', 'C:/') } }
results.report("Fenix '', 'C:/'") { TESTS.times { Fenix::File.expand_path('', 'C:/') } }
+ results.report("Plain '', 'C:/'") { TESTS.times { Fenix::File.expand_path_plain('', 'C:/') } }
results.report("Ruby 'foo', 'C:/'") { TESTS.times { File.expand_path('foo', 'C:/') } }
results.report("Fenix 'foo', 'C:/'") { TESTS.times { Fenix::File.expand_path('foo', 'C:/') } }
+ results.report("Plain 'foo', 'C:/'") { TESTS.times { Fenix::File.expand_path_plain('foo', 'C:/') } }
# So slow, exclude them
if ENV["SLOW"]
results.report("Ruby 'D:/Bar', 'C:/Foo'") { TESTS.times { File.expand_path('D:/Bar', 'C:/Foo') } }
results.report("Fenix 'D:/Bar', 'C:/Foo'") { TESTS.times { Fenix::File.expand_path('D:/Bar', 'C:/Foo') } }
+ results.report("Plain 'D:/Bar', 'C:/Foo'") { TESTS.times { Fenix::File.expand_path_plain('D:/Bar', 'C:/Foo') } }
end
results.report("Ruby '~'") { TESTS.times { File.expand_path('~') } }
results.report("Fenix '~'") { TESTS.times { Fenix::File.expand_path('~') } }
+ results.report("Plain '~'") { TESTS.times { Fenix::File.expand_path_plain('~') } }
results.report("Ruby '~/foo'") { TESTS.times { File.expand_path('~/foo') } }
results.report("Fenix '~/foo'") { TESTS.times { Fenix::File.expand_path('~/foo') } }
+ results.report("Plain '~/foo'") { TESTS.times { Fenix::File.expand_path_plain('~/foo') } }
results.report("Ruby 'foo/'") { TESTS.times { File.expand_path('foo/') } }
results.report("Fenix 'foo/'") { TESTS.times { Fenix::File.expand_path('foo/') } }
+ results.report("Plain 'foo/'") { TESTS.times { Fenix::File.expand_path_plain('foo/') } }
results.report("Ruby '~', 'C:/Foo'") { TESTS.times { File.expand_path('~', 'C:/Foo') } }
results.report("Fenix '~', 'C:/Foo'") { TESTS.times { Fenix::File.expand_path('~', 'C:/Foo') } }
+ results.report("Plain '~', 'C:/Foo'") { TESTS.times { Fenix::File.expand_path_plain('~', 'C:/Foo') } }
results.report("Ruby long_path") { TESTS.times { File.expand_path(long_path) } }
results.report("Fenix long_path") { TESTS.times { Fenix::File.expand_path(long_path) } }
+ results.report("Plain long_path") { TESTS.times { Fenix::File.expand_path_plain(long_path) } }
results.report("Ruby long_path, 'rel'") { TESTS.times { File.expand_path(long_path, 'rel') } }
results.report("Fenix long_path, 'rel'") { TESTS.times { Fenix::File.expand_path(long_path, 'rel') } }
+ results.report("Plain long_path, 'rel'") { TESTS.times { Fenix::File.expand_path_plain(long_path, 'rel') } }
results.report("Ruby long_path, 'C:/Foo'") { TESTS.times { File.expand_path(long_path, 'C:/Foo') } }
results.report("Fenix long_path, 'C:/Foo'") { TESTS.times { Fenix::File.expand_path(long_path, 'C:/Foo') } }
+ results.report("Plain long_path, 'C:/Foo'") { TESTS.times { Fenix::File.expand_path_plain(long_path, 'C:/Foo') } }
results.report("Ruby full_long_path") { TESTS.times { File.expand_path(full_long_path) } }
results.report("Fenix full_long_path") { TESTS.times { Fenix::File.expand_path(full_long_path) } }
+ results.report("Plain full_long_path") { TESTS.times { Fenix::File.expand_path_plain(full_long_path) } }
results.report("Ruby to_path") { TESTS.times { File.expand_path(to_path) } }
results.report("Fenix to_path") { TESTS.times { Fenix::File.expand_path(to_path) } }
+ results.report("Plain to_path") { TESTS.times { Fenix::File.expand_path_plain(to_path) } }
results.report("Ruby to_path, 'rel'") { TESTS.times { File.expand_path(to_path, 'rel') } }
results.report("Fenix to_path, 'rel'") { TESTS.times { Fenix::File.expand_path(to_path, 'rel') } }
+ results.report("Plain to_path, 'rel'") { TESTS.times { Fenix::File.expand_path_plain(to_path, 'rel') } }
results.report("Ruby to_path, 'C:/Foo'") { TESTS.times { File.expand_path(to_path, 'C:/Foo') } }
results.report("Fenix to_path, 'C:/Foo'") { TESTS.times { Fenix::File.expand_path(to_path, 'C:/Foo') } }
+ results.report("Plain to_path, 'C:/Foo'") { TESTS.times { Fenix::File.expand_path_plain(to_path, 'C:/Foo') } }
results.report("Ruby full_to_path") { TESTS.times { File.expand_path(full_to_path) } }
results.report("Fenix full_to_path") { TESTS.times { Fenix::File.expand_path(full_to_path) } }
+ results.report("Plain full_to_path") { TESTS.times { Fenix::File.expand_path_plain(full_to_path) } }
end
diff --git a/ext/fenix/extconf.rb b/ext/fenix/extconf.rb
index 17f71cd..a511332 100644
--- a/ext/fenix/extconf.rb
+++ b/ext/fenix/extconf.rb
@@ -10,6 +10,9 @@ end
# define mininum version of Windows (XP SP1)
$CFLAGS << " -D_WIN32_WINNT=0x0501"
+have_library("shlwapi")
+abort "'PathIsRelativeW' is required." unless have_func("PathIsRelativeW", "shlwapi.h")
+
have_library("kernel32")
NEEDED_FUNCTIONS.each do |f|
diff --git a/ext/fenix/fenix.h b/ext/fenix/fenix.h
index d19d8e7..0c95427 100644
--- a/ext/fenix/fenix.h
+++ b/ext/fenix/fenix.h
@@ -2,9 +2,13 @@
#define FENIX_H
#include <ruby.h>
+#include <ruby/encoding.h>
+#include <wchar.h>
+#include <shlwapi.h>
#include "file.h"
#define IS_DIR_SEPARATOR_P(c) (c == L'\\' || c == L'/')
+#define IS_DIR_UNC_P(c) (IS_DIR_SEPARATOR_P(c[0]) && IS_DIR_SEPARATOR_P(c[1]))
extern VALUE mFenix;
diff --git a/ext/fenix/file.c b/ext/fenix/file.c
index 616a623..38e881d 100644
--- a/ext/fenix/file.c
+++ b/ext/fenix/file.c
@@ -94,7 +94,7 @@ fenix_coerce_to_path(VALUE obj)
// TODO: can we fail allocating memory?
static VALUE
-fenix_file_expand_path(int argc, VALUE *argv)
+fenix_file_expand_path_plain(int argc, VALUE *argv)
{
size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0;
size_t buffer_len = 0;
@@ -269,6 +269,474 @@ fenix_file_expand_path(int argc, VALUE *argv)
return result;
}
+/* Convert the path from char to wchar with specified code page */
+static inline void
+fenix_path_to_wchar(VALUE path, wchar_t **wpath, wchar_t **wpath_pos, size_t *wpath_len, UINT code_page)
+{
+ size_t size;
+
+ if (NIL_P(path))
+ return;
+
+ size = MultiByteToWideChar(code_page, 0, RSTRING_PTR(path), -1, NULL, 0) + 1;
+ *wpath = (wchar_t *)malloc(size * sizeof(wchar_t));
+ if (wpath_pos)
+ *wpath_pos = *wpath;
+
+ MultiByteToWideChar(code_page, 0, RSTRING_PTR(path), -1, *wpath, size);
+ *wpath_len = size - 2; // wcslen(*wpath);
+}
+
+/* Remove trailing invalid ':$DATA' of the path. */
+static inline size_t
+fenix_remove_invalid_alternative_data(wchar_t *wfullpath, size_t size) {
+ static const wchar_t prime[] = L":$DATA";
+ enum {prime_len = (sizeof(prime) / sizeof(wchar_t)) -1};
+
+ if (size <= prime_len || _wcsnicmp(wfullpath + size - prime_len, prime, prime_len) != 0)
+ return size;
+
+ // wprintf(L"remove trailng ':$DATA': %s, %s\n", wfullpath, &wfullpath[size - prime_len]);
+ /* alias of stream */
+ /* get rid of a bug of x64 VC++ */
+ if (wfullpath[size - (prime_len + 1)] == ':') {
+ /* remove trailing '::$DATA' */
+ size -= prime_len + 1; /* prime */
+ wfullpath[size] = L'\0';
+ // wprintf(L"removed trailng '::$DATA': %s\n", wfullpath);
+ } else {
+ /* remove trailing ':$DATA' of paths like '/aa:a:$DATA' */
+ wchar_t *pos = wfullpath + size - (prime_len + 1);
+ while (!IS_DIR_SEPARATOR_P(*pos) && pos != wfullpath) {
+ if (*pos == L':') {
+ size -= prime_len; /* alternative */
+ wfullpath[size] = L'\0';
+ // wprintf(L"removed trailng ':$DATA': %s\n", wfullpath);
+ break;
+ }
+ pos--;
+ }
+ }
+ return size;
+}
+
+/*
+ Replace the last part of the path to long name.
+ We try to avoid to call FindFirstFileW() since it takes long time.
+*/
+static inline size_t
+fenix_replace_to_long_name(wchar_t **wfullpath, size_t size, int heap) {
+ WIN32_FIND_DATAW find_data;
+ HANDLE find_handle;
+
+ /*
+ Skip long name conversion if the path is already long name.
+ Short name is 8.3 format.
+ http://en.wikipedia.org/wiki/8.3_filename
+ This check can be skipped for directory components that have file
+ extensions longer than 3 characters, or total lengths longer than
+ 12 characters.
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
+ */
+ size_t const max_short_name_size = 8 + 1 + 3;
+ size_t const max_extension_size = 3;
+ size_t path_len = 1, extension_len = 0;
+ wchar_t *pos = *wfullpath + size - 1;
+ while (!IS_DIR_SEPARATOR_P(*pos) && pos != *wfullpath) {
+ if (!extension_len && *pos == L'.') {
+ extension_len = path_len - 1;
+ }
+ if (path_len > max_short_name_size || extension_len > max_extension_size) {
+ // wprintf(L"skip long name conversion: %s\n", pos);
+ return size;
+ }
+ path_len++;
+ pos--;
+ }
+
+ /*
+ Avoid FindFirstFileW over non-existing path.
+ GetFileAttributesW is faster to check if path doesn't exists.
+ */
+ if (GetFileAttributesW(*wfullpath) == INVALID_FILE_ATTRIBUTES)
+ return size;
+
+ find_handle = FindFirstFileW(*wfullpath, &find_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ size_t trail_pos = wcslen(*wfullpath);
+ size_t file_len = wcslen(find_data.cFileName);
+
+ FindClose(find_handle);
+ while (trail_pos > 0) {
+ if (IS_DIR_SEPARATOR_P((*wfullpath)[trail_pos]))
+ break;
+ trail_pos--;
+ }
+ size = trail_pos + 1 + file_len;
+ if ((size + 1) > sizeof(*wfullpath) / sizeof((*wfullpath)[0])) {
+ wchar_t *buf = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
+ wcsncpy(buf, *wfullpath, trail_pos + 1);
+ if (heap)
+ free(*wfullpath);
+ *wfullpath = buf;
+ }
+ wcsncpy(*wfullpath + trail_pos + 1, find_data.cFileName, file_len + 1);
+ }
+ return size;
+}
+
+/* Return system code page. */
+static inline UINT system_code_page() {
+ return AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+}
+
+/* cache 'encoding name' => 'code page' into a hash */
+static VALUE rb_code_page;
+
+/*
+ Return code page number of the encoding.
+ Cache code page into a hash for performance since finding the code page in
+ Encoding#names is slow.
+*/
+static UINT
+fenix_code_page(rb_encoding *enc)
+{
+ VALUE code_page_value, name_key;
+ VALUE encoding, names_ary = Qundef, name;
+ ID names;
+ long i;
+
+ if (!enc)
+ return system_code_page();
+
+ name_key = rb_usascii_str_new2(rb_enc_name(enc));
+ code_page_value = rb_hash_aref(rb_code_page, name_key);
+ if (code_page_value != Qnil) {
+ // printf("cached code page: %i\n", FIX2INT(code_page_value));
+ if (FIX2INT(code_page_value) == -1) {
+ return system_code_page();
+ } else {
+ return (UINT)FIX2INT(code_page_value);
+ }
+ }
+
+ encoding = rb_enc_from_encoding(enc);
+ if (!NIL_P(encoding)) {
+ CONST_ID(names, "names");
+ names_ary = rb_funcall(encoding, names, 0);
+ }
+
+ if (names_ary != Qundef) {
+ for (i = 0; i < RARRAY_LEN(names_ary); i++) {
+ name = RARRAY_PTR(names_ary)[i];
+ if (strncmp("CP", RSTRING_PTR(name), 2) == 0) {
+ int code_page = atoi(RSTRING_PTR(name) + 2);
+ rb_hash_aset(rb_code_page, name_key, INT2FIX(code_page));
+ return (UINT)code_page;
+ }
+ }
+ }
+
+ rb_hash_aset(rb_code_page, name_key, INT2FIX(-1));
+ return system_code_page();
+}
+
+#define PATH_BUFFER_SIZE MAX_PATH * 2
+
+// TODO: can we fail allocating memory?
+static VALUE
+fenix_file_expand_path(int argc, VALUE *argv)
+{
+ size_t size = 0, wpath_len = 0, wdir_len = 0, whome_len = 0;
+ size_t buffer_len = 0;
+ char *fullpath = NULL;
+ wchar_t *wfullpath = NULL, *wpath = NULL, *wpath_pos = NULL, *wdir = NULL;
+ wchar_t *whome = NULL, *buffer = NULL, *buffer_pos = NULL;
+ UINT cp;
+ VALUE result = Qnil, path = Qnil, dir = Qnil;
+ wchar_t wfullpath_buffer[PATH_BUFFER_SIZE];
+ wchar_t path_drive = L'\0', dir_drive = L'\0';
+ int ignore_dir = 0;
+ rb_encoding *path_encoding;
+
+ // 1: convert to long name
+ // 0: not convert to long name
+ int to_long_name = 0;
+
+ VALUE to_long = Qnil;
+
+ // retrieve path and dir from argv
+ rb_scan_args(argc, argv, "12", &path, &dir, &to_long);
+
+ // convert to_long to int
+ if (!NIL_P(to_long))
+ to_long_name = FIX2INT(to_long);
+
+ // get path encoding
+ if (NIL_P(dir)) {
+ path_encoding = rb_enc_get(path);
+ } else {
+ path_encoding = rb_enc_check(path, dir);
+ }
+ cp = fenix_code_page(path_encoding);
+ // printf("code page: %i\n", cp);
+
+ // coerce them to string
+ path = fenix_coerce_to_path(path);
+
+ // convert char * to wchar_t
+ // path
+ fenix_path_to_wchar(path, &wpath, &wpath_pos, &wpath_len, cp);
+ // wprintf(L"wpath: '%s' with (%i) characters long.\n", wpath, wpath_len);
+
+ /* determine if we need the user's home directory */
+ if ((wpath_len == 1 && wpath_pos[0] == L'~') ||
+ (wpath_len >= 2 && wpath_pos[0] == L'~' && IS_DIR_SEPARATOR_P(wpath_pos[1]))) {
+ // wprintf(L"wpath requires expansion.\n");
+ whome = fenix_home_dir();
+ if (whome == NULL) {
+ free(wpath);
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
+ }
+ if (PathIsRelativeW(whome)) {
+ free(wpath);
+ rb_raise(rb_eArgError, "non-absolute home");
+ }
+ whome_len = wcslen(whome);
+
+ if (wpath_len == 1)
+ to_long_name = 0;
+
+ // wprintf(L"whome: '%s' with (%i) characters long.\n", whome, whome_len);
+
+ /* ignores dir since we are expading home */
+ ignore_dir = 1;
+
+ /* exclude ~ from the result */
+ wpath_pos++;
+ wpath_len--;
+
+ /* exclude separator if present */
+ if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
+ // wprintf(L"excluding expansion character and separator\n");
+ wpath_pos++;
+ wpath_len--;
+ }
+ } else if (wpath_len >= 2 && wpath_pos[1] == L':') {
+ if (wpath_len >= 3 && IS_DIR_SEPARATOR_P(wpath_pos[2])) {
+ /* ignore dir since path contains a drive letter and a root slash */
+ // wprintf(L"Ignore dir since we have drive letter and root slash\n");
+ ignore_dir = 1;
+ } else {
+ /* determine if we ignore dir or not later */
+ path_drive = wpath_pos[0];
+ }
+ } else if (wpath_len >= 2 && wpath_pos[0] == L'~') {
+ wchar_t *wuser = wpath_pos + 1;
+ wchar_t *pos = wuser;
+ char *user;
+
+ while (!IS_DIR_SEPARATOR_P(*pos) && *pos != '\0')
+ pos++;
+
+ *pos = '\0';
+ size = WideCharToMultiByte(cp, 0, wuser, -1, NULL, 0, NULL, NULL);
+ user = (char *)malloc(size * sizeof(char));
+ WideCharToMultiByte(cp, 0, wuser, -1, user, size, NULL, NULL);
+
+ /* convert to VALUE and set the path encoding */
+ result = rb_enc_str_new(user, size - 1, path_encoding);
+
+ free(wpath);
+ if (user)
+ free(user);
+
+ rb_raise(rb_eArgError, "can't find user %s", StringValuePtr(result));
+ }
+
+ /* convert dir */
+ if (!ignore_dir && !NIL_P(dir)) {
+ // coerce them to string
+ dir = fenix_coerce_to_path(dir);
+
+ // convert char * to wchar_t
+ // dir
+ fenix_path_to_wchar(dir, &wdir, NULL, &wdir_len, cp);
+ // wprintf(L"wdir: '%s' with (%i) characters long.\n", wdir, wdir_len);
+
+ if (wdir_len >= 2 && wdir[1] == L':') {
+ dir_drive = wdir[0];
+ if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
+ wdir_len = 2;
+ }
+ } else if (wdir_len >= 2 && IS_DIR_UNC_P(wdir)) {
+ /* UNC path */
+ if (wpath_len && IS_DIR_SEPARATOR_P(wpath_pos[0])) {
+ /* cut the UNC path tail to '//host/share' */
+ size_t separators = 0;
+ size_t pos = 2;
+ while (pos < wdir_len && separators < 2) {
+ if (IS_DIR_SEPARATOR_P(wdir[pos])) {
+ separators++;
+ }
+ pos++;
+ }
+ if (separators == 2)
+ wdir_len = pos - 1;
+ // wprintf(L"UNC wdir: '%s' with (%i) characters.\n", wdir, wdir_len);
+ }
+ }
+ }
+
+ /* determine if we ignore dir or not */
+ if (!ignore_dir && path_drive && dir_drive) {
+ if (towupper(path_drive) == towupper(dir_drive)) {
+ /* exclude path drive letter to use dir */
+ // wprintf(L"excluding path drive letter\n");
+ wpath_pos += 2;
+ wpath_len -= 2;
+ } else {
+ /* ignore dir since path drive is different from dir drive */
+ ignore_dir = 1;
+ wdir_len = 0;
+ }
+ }
+
+ // wprintf(L"wpath_len: %i\n", wpath_len);
+ // wprintf(L"wdir_len: %i\n", wdir_len);
+ // wprintf(L"whome_len: %i\n", whome_len);
+
+ buffer_len = wpath_len + 1 + wdir_len + 1 + whome_len + 1;
+ // wprintf(L"buffer_len: %i\n", buffer_len + 1);
+
+ buffer = buffer_pos = (wchar_t *)malloc((buffer_len + 1) * sizeof(wchar_t));
+
+ /* add home */
+ if (whome_len) {
+ // wprintf(L"Copying whome...\n");
+ wcsncpy(buffer_pos, whome, whome_len);
+ buffer_pos += whome_len;
+ }
+
+ /* Add separator if required */
+ if (whome_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
+ // wprintf(L"Adding separator after whome\n");
+ buffer_pos[0] = L'\\';
+ buffer_pos++;
+ }
+
+ if (wdir_len) {
+ // wprintf(L"Copying wdir...\n");
+ wcsncpy(buffer_pos, wdir, wdir_len);
+ buffer_pos += wdir_len;
+ }
+
+ /* add separator if required */
+ if (wdir_len && wcsrchr(L"\\/:", buffer_pos[-1]) == NULL) {
+ // wprintf(L"Adding separator after wdir\n");
+ buffer_pos[0] = L'\\';
+ buffer_pos++;
+ }
+
+ /* now deal with path */
+ if (wpath_len) {
+ // wprintf(L"Copying wpath...\n");
+ wcsncpy(buffer_pos, wpath_pos, wpath_len);
+ buffer_pos += wpath_len;
+ }
+
+ if ((wpath_len == 1 && buffer[0] == L'.') || (wpath_len == 2 && buffer[1] == L':'))
+ to_long_name = 0;
+
+ /* GetFullPathNameW requires at least "." to determine current directory */
+ if (wpath_len == 0) {
+ // wprintf(L"Adding '.' to buffer\n");
+ buffer_pos[0] = L'.';
+ buffer_pos++;
+ to_long_name = 0;
+ }
+
+ /* Ensure buffer is NULL terminated */
+ buffer_pos[0] = L'\0';
+
+ // wprintf(L"buffer: '%s'\n", buffer);
+
+ // FIXME: Make this more robust
+ // Determine require buffer size
+ size = GetFullPathNameW(buffer, PATH_BUFFER_SIZE, wfullpath_buffer, NULL);
+ if (size) {
+ if (size > PATH_BUFFER_SIZE) {
+ // allocate enough memory to contain the response
+ wfullpath = (wchar_t *)malloc(size * sizeof(wchar_t));
+ size = GetFullPathNameW(buffer, size, wfullpath, NULL);
+ } else {
+ wfullpath = wfullpath_buffer;
+ }
+
+
+ /* Calculate the new size and leave the garbage out */
+ // size = wcslen(wfullpath);
+
+ /* Remove any trailing slashes */
+ if (IS_DIR_SEPARATOR_P(wfullpath[size - 1]) && wfullpath[size - 2] != L':') {
+ // wprintf(L"Removing trailing slash\n");
+ size -= 1;
+ wfullpath[size] = L'\0';
+ }
+
+ /* Remove any trailing dot */
+ if (wfullpath[size - 1] == L'.') {
+ // wprintf(L"Removing trailing dot\n");
+ size -= 1;
+ wfullpath[size] = L'\0';
+ }
+
+ /* removes trailing invalid ':$DATA' */
+ size = fenix_remove_invalid_alternative_data(wfullpath, size);
+
+ /* Replace the trailing path to long name */
+ if (to_long_name)
+ size = fenix_replace_to_long_name(&wfullpath, size, (wfullpath != wfullpath_buffer));
+
+
+ // sanitize backslashes with forwardslashes
+ fenix_replace_wchar(wfullpath, L'\\', L'/');
+ // wprintf(L"wfullpath: '%s'\n", wfullpath);
+
+ // What CodePage should we use?
+ // cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+
+ // convert to char *
+ size = WideCharToMultiByte(cp, 0, wfullpath, -1, NULL, 0, NULL, NULL);
+ fullpath = (char *)malloc(size * sizeof(char));
+ WideCharToMultiByte(cp, 0, wfullpath, -1, fullpath, size, NULL, NULL);
+
+ /* convert to VALUE and set the path encoding */
+ result = rb_enc_str_new(fullpath, size - 1, path_encoding);
+ }
+
+ // TODO: better cleanup
+ if (buffer)
+ free(buffer);
+
+ if (wpath)
+ free(wpath);
+
+ if (wdir)
+ free(wdir);
+
+ if (whome)
+ free(whome);
+
+ if (wfullpath && wfullpath != wfullpath_buffer)
+ free(wfullpath);
+
+ if (fullpath)
+ free(fullpath);
+
+ return result;
+}
+
static VALUE
fenix_file_replace()
{
@@ -284,4 +752,10 @@ void Init_fenix_file()
rb_define_singleton_method(cFenixFile, "replace!", fenix_file_replace, 0);
rb_define_singleton_method(cFenixFile, "expand_path", fenix_file_expand_path, -1);
+ rb_define_singleton_method(cFenixFile, "expand_path_plain", fenix_file_expand_path_plain, -1);
+
+ rb_code_page = rb_hash_new();
+
+ /* prevent GC removing rb_code_page */
+ rb_gc_register_mark_object(rb_code_page);
}
diff --git a/spec/fenix/file_spec.rb b/spec/fenix/file_spec.rb
index 3184a14..72ef406 100644
--- a/spec/fenix/file_spec.rb
+++ b/spec/fenix/file_spec.rb
@@ -1,10 +1,22 @@
require "spec_helper"
describe Fenix::File do
+# subject { File }
subject { Fenix::File }
let(:base) { Dir.pwd }
let(:tmpdir) { "C:/Temporary" }
let(:rootdir) { "C:/" }
+ let(:drive) { Dir.pwd[%r'\A(?:[a-z]:|//[^/]+/[^/]+)'i] }
+ let(:os_version) do
+ require 'win32ole'
+ locator = WIN32OLE.new("WbemScripting.SWbemLocator")
+ server = locator.ConnectServer(".","root/cimv2")
+ version = nil
+ server.ExecQuery("select * from Win32_OperatingSystem").each do |os|
+ version = os.Version
+ end
+ version
+ end
describe "expand_path" do
it "converts an empty pathname into absolute current pathname" do
@@ -34,6 +46,7 @@ describe Fenix::File do
# this spec is not valid, a.. is not a valid file or directory name so
# b can't be inside of it
+ skip "XP doesn't pass the following. Result is '#{base}/a./b'" if os_version =~ /^5\.1/
subject.expand_path('a../b').must_equal File.join(base, 'a../b')
end
@@ -59,16 +72,70 @@ describe Fenix::File do
subject.expand_path("#{rootdir}/foo.rb/").must_equal File.join(rootdir, "foo.rb")
end
+ it "removes trailing spaces from absolute path" do
+ subject.expand_path("#{rootdir}/a ").must_equal File.join(rootdir, "a")
+ end
+
+ it "removes trailing dots from absolute path" do
+ subject.expand_path("#{rootdir}/a.").must_equal File.join(rootdir, "a")
+ end
+
+ it "removes trailing invalid ':$DATA' from absolute path" do
+ subject.expand_path("#{rootdir}/aaa::$DATA").must_equal File.join(rootdir, "aaa")
+ subject.expand_path("#{rootdir}/aa:a:$DATA").must_equal File.join(rootdir, "aa:a")
+ subject.expand_path("#{rootdir}/aaa:$DATA").must_equal File.join(rootdir, "aaa:$DATA")
+ end
+
+ it "converts a pathname with a drive letter but no slash [ruby-core:31591]" do
+ subject.expand_path('c:').must_match /\Ac:\//i
+ end
+
+ it "converts a pathname with a drive letter ignoring different drive dir [ruby-core:42177]" do
+ subject.expand_path('c:foo', 'd:/bar').must_match /\Ac:\//i
+ end
+
+ it "converts a pathname with a drive letter using same drive dir [ruby-core:42177]" do
+ subject.expand_path('c:foo', 'c:/bar').must_match %r'\Ac:/bar/foo\z'i
+ end
+
+ it "converts a pathname which starts with a slash using dir's drive" do
+ subject.expand_path('/foo', "z:/bar").must_match %r"\Az:/foo\z"i
+ end
+
+ it "converts a dot with UNC dir" do
+ subject.expand_path('.', "//").must_equal "//"
+ end
+
+ it "converts a pathname which starts with a slash using '//host/share'" do
+ subject.expand_path('/foo', "//host/share/bar").must_match %r"\A//host/share/foo\z"i
+ end
+
+ it "converts a pathname which starts with a slash using a current drive" do
+ subject.expand_path('/foo').must_match %r"\A#{drive}/foo\z"i
+ end
+
describe "~/" do
let(:home) { "C:/UserHome" }
+ let(:home_drive) { nil }
+ let(:home_path) { nil }
+ let(:user_profile) { nil }
before :each do
@old_home = ENV["HOME"]
+ @old_home_drive = ENV["HOMEDRIVE"]
+ @old_home_path = ENV["HOMEPATH"]
+ @old_user_profile = ENV["USERPROFILE"]
ENV["HOME"] = home
+ ENV["HOMEDRIVE"] = home_drive
+ ENV["HOMEPATH"] = home_path
+ ENV["USERPROFILE"] = user_profile
end
after :each do
ENV["HOME"] = @old_home if @old_home
+ ENV["HOMEDRIVE"] = @old_home_drive if @old_home_drive
+ ENV["HOMEPATH"] = @old_home_path if @old_home_path
+ ENV["USERPROFILE"] = @old_user_profile if @old_user_profile
end
it "converts a pathname to an absolute pathname, using ~ (home) as base" do
@@ -83,16 +150,25 @@ describe Fenix::File do
str.must_equal "~/a"
end
+ describe "(nil)" do
+ let(:home) { nil }
+ let(:home_drive) { nil }
+ let(:home_path) { nil }
+ let(:user_profile) { nil }
+
+ it "raises ArgumentError when home is nothing" do
+ proc { subject.expand_path("~") }.must_raise ArgumentError
+ end
+ end
+
describe "(non-absolute)" do
let(:home) { "." }
it "raises ArgumentError when having non-absolute home directories" do
- skip "implement me"
proc { subject.expand_path("~") }.must_raise ArgumentError
end
it "raises ArgumentError when having non-absolute home of a specified user" do
- skip "implement me"
proc { subject.expand_path("~anything") }.must_raise ArgumentError
end
end
@@ -100,7 +176,6 @@ describe Fenix::File do
describe "~username" do
it "raises ArgumentError for any supplied username [ruby-core:39597]" do
- skip "implement me"
proc { subject.expand_path("~anything") }.must_raise ArgumentError
end
end
@@ -151,8 +226,33 @@ describe Fenix::File do
end
it "expands a shortname directory into the full version [ruby-core:39504]" do
- skip "implement me"
- subject.expand_path(@shortname).must_include long_name
+ if subject == Fenix::File
+ subject.expand_path(@shortname, nil, 1).must_include long_name
+ else
+ subject.expand_path(@shortname).must_include long_name
+ end
+ end
+ end
+
+ describe "encoding" do
+ it "expands using path encoding not file system encoding" do
+ if Encoding.find("filesystem") == Encoding::CP1251
+ a = "#{drive}/\u3042\u3044\u3046\u3048\u304a".encode("cp932")
+ else
+ a = "#{drive}/\u043f\u0440\u0438\u0432\u0435\u0442".encode("cp1251")
+ end
+ subject.expand_path(a).must_equal a
+ end
+
+ it "removes trailing backslashes unless it's not in multibyte characters" do
+ a = "#{drive}/\225\\\\"
+ if File::ALT_SEPARATOR == '\\'
+ [%W"cp437 #{drive}/\225", %W"cp932 #{drive}/\225\\"]
+ else
+ [["cp437", a], ["cp932", a]]
+ end.each do |cp, expected|
+ subject.expand_path(a.dup.force_encoding(cp)).must_equal expected.force_encoding(cp), cp
+ end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment