File: loader_test.rb

package info (click to toggle)
ruby-zeitwerk 2.7.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 732 kB
  • sloc: ruby: 6,240; makefile: 4
file content (126 lines) | stat: -rw-r--r-- 3,141 bytes parent folder | download
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
# frozen_string_literal: true

class LoaderTest < Minitest::Test
  TMP_DIR = File.expand_path("../tmp", __dir__)

  attr_reader :loader

  def setup
    @loader = new_loader(setup: false)
  end

  # We enable reloading in the reloaders of the test suite to have a robust
  # cleanup of constants.
  #
  # There are gems that allow you to run tests in forked processes and you do
  # not need to care, but JRuby does not support forking, and I prefer to be
  # ready for the day in which Zeitwerk runs on JRuby.
  def new_loader(dirs: [], namespace: Object, enable_reloading: true, setup: true)
    Zeitwerk::Loader.new.tap do |loader|
      Array(dirs).each do |dir|
        loader.push_dir(dir, namespace: namespace)
      end
      loader.enable_reloading if enable_reloading
      loader.setup            if setup
    end
  end

  def reset_constants
    Zeitwerk::Registry.loaders.each do |loader|
      begin
        loader.unload
      rescue Zeitwerk::SetupRequired
      end
    end
  end

  def reset_registry
    Zeitwerk::Registry.loaders.clear
    Zeitwerk::Registry.gem_loaders_by_root_file.clear
    Zeitwerk::Registry.autoloads.clear
    Zeitwerk::Registry.inceptions.clear
  end

  def reset_explicit_namespace
    Zeitwerk::Registry.explicit_namespaces.clear
  end

  def teardown
    reset_constants
    reset_registry
    reset_explicit_namespace
  end

  def mkdir_test
    FileUtils.rm_rf(TMP_DIR)
    FileUtils.mkdir_p(TMP_DIR)
  end

  def with_files(files, rm: true)
    mkdir_test

    Dir.chdir(TMP_DIR) do
      files.each do |fname, contents|
        FileUtils.mkdir_p(File.dirname(fname))
        if contents
          File.write(fname, contents)
        else
          FileUtils.touch(fname)
        end
      end
      yield
    end
  ensure
    mkdir_test if rm
  end

  def with_load_path(dirs = loader.dirs)
    dirs = Array(dirs).map { |dir| File.expand_path(dir) }
    dirs.each { |dir| $LOAD_PATH.push(dir) }
    yield
  ensure
    dirs.each { |dir| $LOAD_PATH.delete(dir) }
  end

  def with_setup(files = [], dirs: nil, namespace: Object, load_path: nil, rm: true)
    with_files(files, rm: rm) do
      dirs ||= files.map do |file|
        file[0] =~ %r{\A(rd\d+)/} ? $1 : "."
      end.uniq
      dirs.each { |dir| loader.push_dir(dir, namespace: namespace) }

      files.each do |file|
        if File.basename(file[0]) == "ignored.rb"
          loader.ignore(file[0])
        elsif file[0] =~ %r{\A(ignored|.+/ignored)/}
          loader.ignore($1)
        end

        if file[0] =~ %r{\A(collapsed|.+/collapsed)/}
          loader.collapse($1)
        end
      end

      loader.setup
      if load_path
        with_load_path(load_path) { yield }
      else
        yield
      end
    end
  end

  def required?(file_or_files)
    if file_or_files.is_a?(String)
      $LOADED_FEATURES.include?(File.expand_path(file_or_files, TMP_DIR))
    elsif file_or_files[0].is_a?(String)
      required?(file_or_files[0])
    else
      file_or_files.all? { |f| required?(f) }
    end
  end

  def assert_abspath(expected, actual)
    assert_equal(File.expand_path(expected, TMP_DIR), actual)
  end
end