File: cache_test.rb

package info (click to toggle)
ruby-bootsnap 1.18.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 524 kB
  • sloc: ruby: 3,718; ansic: 756; sh: 14; makefile: 9
file content (206 lines) | stat: -rw-r--r-- 7,339 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# frozen_string_literal: true

require "test_helper"

module Bootsnap
  module LoadPathCache
    class CacheTest < Minitest::Test
      include LoadPathCacheHelper

      def setup
        super
        @dir1 = File.realpath(Dir.mktmpdir)
        @dir2 = File.realpath(Dir.mktmpdir)
        FileUtils.touch("#{@dir1}/a.rb")
        FileUtils.mkdir_p("#{@dir1}/foo/bar")
        FileUtils.touch("#{@dir1}/foo/bar/baz.rb")
        FileUtils.touch("#{@dir2}/b.rb")
        FileUtils.touch("#{@dir1}/conflict.rb")
        FileUtils.touch("#{@dir2}/conflict.rb")
        FileUtils.touch("#{@dir1}/dl#{DLEXT}")
        FileUtils.touch("#{@dir1}/both.rb")
        FileUtils.touch("#{@dir1}/both#{DLEXT}")
        FileUtils.touch("#{@dir1}/béé.rb")
      end

      def teardown
        FileUtils.rm_rf(@dir1)
        FileUtils.rm_rf(@dir2)
      end

      # dev.yml specifies 2.3.3 and this test assumes it. Failures on other
      # versions aren't a big deal, but feel free to fix the test.
      def test_builtin_features
        cache = Cache.new(NullCache, [])
        assert_equal false, cache.find("thread")
        assert_equal false, cache.find("thread.rb")
        assert_equal false, cache.find("enumerator")

        if truffleruby?
          assert_equal false, cache.find("enumerator.rb")
        else
          assert_equal false, cache.find("enumerator.so")
          if RUBY_PLATFORM =~ /darwin/
            assert_equal false, cache.find("enumerator.bundle")
          else
            assert_same FALLBACK_SCAN, cache.find("enumerator.bundle")
          end
        end

        bundle = RUBY_PLATFORM =~ /darwin/ ? "bundle" : "so"

        # These are not present in TruffleRuby but that means they will still return falsey.
        refute(cache.find("thread.#{bundle}"))
        refute(cache.find("enumerator.rb"))
        refute(cache.find("encdb.#{bundle}"))
      end

      def test_simple
        po = [@dir1]
        cache = Cache.new(NullCache, po)
        assert_equal("#{@dir1}/a.rb", cache.find("a"))
        cache.push_paths(po, @dir2)
        assert_equal("#{@dir2}/b.rb", cache.find("b"))
      end

      def test_extension_append_for_relative_paths
        po = [@dir1]
        cache = Cache.new(NullCache, po)
        dir1_basename = File.basename(@dir1)
        Dir.chdir(@dir1) do
          assert_equal("#{@dir1}/a.rb",       cache.find("./a"))
          assert_equal("#{@dir1}/a.rb",       cache.find("../#{dir1_basename}/a"))
          assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("./dl"))
          assert_equal("#{@dir1}/enoent",     cache.find("./enoent"))
        end
      end

      def test_unshifted_paths_have_higher_precedence
        po = [@dir1]
        cache = Cache.new(NullCache, po)
        assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
        cache.unshift_paths(po, @dir2)
        assert_equal("#{@dir2}/conflict.rb", cache.find("conflict"))
      end

      def test_pushed_paths_have_lower_precedence
        po = [@dir1]
        cache = Cache.new(NullCache, po)
        assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
        cache.push_paths(po, @dir2)
        assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
      end

      def test_directory_caching
        cache = Cache.new(NullCache, [@dir1])
        assert_equal(@dir1, cache.load_dir("foo"))
        assert_equal(@dir1, cache.load_dir("foo/bar"))
        assert_nil(cache.load_dir("bar"))
      end

      def test_extension_permutations
        cache = Cache.new(NullCache, [@dir1])
        assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("dl"))
        assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("dl#{DLEXT}"))
        assert_equal("#{@dir1}/both.rb", cache.find("both"))
        assert_equal("#{@dir1}/both.rb", cache.find("both.rb"))
        assert_equal("#{@dir1}/both#{DLEXT}", cache.find("both#{DLEXT}"))
      end

      def test_relative_paths_rescanned
        Dir.chdir(@dir2) do
          cache = Cache.new(NullCache, %w(foo))
          refute(cache.find("bar/baz"))
          Dir.chdir(@dir1) do
            # one caveat here is that you get the actual path back when
            # resolving relative paths. On darwin, this means that
            # /var/folders/... comes back as /private/var/folders/... -- In
            # production, this should be fine, but for this test to pass, we
            # have to resolve it.
            assert_equal(File.realpath("#{@dir1}/foo/bar/baz.rb"), cache.find("bar/baz"))
          end
        end
      end

      def test_development_mode
        time = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i

        # without development_mode, no refresh
        dev_no_cache = Cache.new(NullCache, [@dir1], development_mode: false)
        dev_yes_cache = Cache.new(NullCache, [@dir1], development_mode: true)

        FileUtils.touch("#{@dir1}/new.rb")

        dev_no_cache.stubs(:now).returns(time + 31)
        refute(dev_no_cache.find("new"))

        dev_yes_cache.stubs(:now).returns(time + 28)
        assert_same Bootsnap::LoadPathCache::FALLBACK_SCAN, dev_yes_cache.find("new")
        dev_yes_cache.stubs(:now).returns(time + 31)
        assert(dev_yes_cache.find("new"))
      end

      def test_path_obj_equal?
        path_obj = []
        cache = Cache.new(NullCache, path_obj)

        path_obj.unshift(@dir1)

        assert_equal("#{@dir1}/a.rb", cache.find("a"))
      end

      if RbConfig::CONFIG["host_os"] !~ /mswin|mingw|cygwin/
        # https://github.com/ruby/ruby/pull/4061
        # https://bugs.ruby-lang.org/issues/17517
        OS_ASCII_PATH_ENCODING = RUBY_VERSION >= "3.1" ? Encoding::UTF_8 : Encoding::US_ASCII

        def test_path_encoding
          unless Encoding.default_external == Encoding::UTF_8
            # Encoding.default_external != Encoding::UTF_8 is likely a misconfiguration or a barebone system.
            # Supporting this use case would have an overhead for relatively little gain.
            skip "Encoding.default_external == #{Encoding.default_external}, expected Encoding::UTF_8."
          end

          po = [@dir1]
          cache = Cache.new(NullCache, po)

          path = cache.find("a")

          assert_equal("#{@dir1}/a.rb", path)
          require path
          internal_path = $LOADED_FEATURES.last
          assert_equal(OS_ASCII_PATH_ENCODING, internal_path.encoding)
          # TruffleRuby object is a copy and the encoding resets to utf-8.
          assert_equal(OS_ASCII_PATH_ENCODING, path.encoding) unless truffleruby?
          File.write(path, "")

          if truffleruby?
            assert_equal path, internal_path
          else
            assert_same path, internal_path
          end

          utf8_path = cache.find("béé")
          assert_equal("#{@dir1}/béé.rb", utf8_path)
          require utf8_path
          internal_utf8_path = $LOADED_FEATURES.last
          assert_equal(Encoding::UTF_8, internal_utf8_path.encoding)
          assert_equal(Encoding::UTF_8, utf8_path.encoding)
          File.write(utf8_path, "")

          if truffleruby?
            assert_equal utf8_path, internal_utf8_path
          else
            assert_same utf8_path, internal_utf8_path
          end
        end
      end

      private

      def truffleruby?
        RUBY_ENGINE == "truffleruby"
      end
    end
  end
end