aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/test/evented_file_update_checker_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/test/evented_file_update_checker_test.rb')
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb225
1 files changed, 225 insertions, 0 deletions
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
new file mode 100644
index 0000000000..b2d5eb94c2
--- /dev/null
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -0,0 +1,225 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "pathname"
+require "file_update_checker_shared_tests"
+
+class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
+ include FileUpdateCheckerSharedTests
+
+ def setup
+ skip if ENV["LISTEN"] == "0"
+ require "listen"
+ super
+ end
+
+ def new_checker(files = [], dirs = {}, &block)
+ ActiveSupport::EventedFileUpdateChecker.new(files, dirs, &block).tap do |c|
+ wait
+ end
+ end
+
+ def teardown
+ super
+ Listen.stop
+ end
+
+ def wait
+ sleep 1
+ end
+
+ def touch(files)
+ super
+ wait # wait for the events to fire
+ end
+
+ test "notifies forked processes" do
+ jruby_skip "Forking not available on JRuby"
+
+ FileUtils.touch(tmpfiles)
+
+ checker = new_checker(tmpfiles) { }
+ assert_not_predicate checker, :updated?
+
+ # Pipes used for flow control across fork.
+ boot_reader, boot_writer = IO.pipe
+ touch_reader, touch_writer = IO.pipe
+
+ pid = fork do
+ assert_predicate checker, :updated?
+
+ # Clear previous check value.
+ checker.execute
+ assert_not_predicate checker, :updated?
+
+ # Fork is booted, ready for file to be touched
+ # notify parent process.
+ boot_writer.write("booted")
+
+ # Wait for parent process to signal that file
+ # has been touched.
+ IO.select([touch_reader])
+
+ assert_predicate checker, :updated?
+ end
+
+ assert pid
+
+ # Wait for fork to be booted before touching files.
+ IO.select([boot_reader])
+ touch(tmpfiles)
+
+ # Notify fork that files have been touched.
+ touch_writer.write("touched")
+
+ assert_predicate checker, :updated?
+
+ Process.wait(pid)
+ end
+
+ test "updated should become true when nonexistent directory is added later" do
+ Dir.mktmpdir do |dir|
+ watched_dir = File.join(dir, "app")
+ unwatched_dir = File.join(dir, "node_modules")
+ not_exist_watched_dir = File.join(dir, "test")
+
+ Dir.mkdir(watched_dir)
+ Dir.mkdir(unwatched_dir)
+
+ checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { }
+
+ FileUtils.touch(File.join(watched_dir, "a.rb"))
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
+
+ Dir.mkdir(not_exist_watched_dir)
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
+
+ FileUtils.touch(File.join(unwatched_dir, "a.rb"))
+ wait
+ assert_not_predicate checker, :updated?
+ assert_not checker.execute_if_updated
+ end
+ end
+end
+
+class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase
+ def pn(path)
+ Pathname.new(path)
+ end
+
+ setup do
+ @ph = ActiveSupport::EventedFileUpdateChecker::PathHelper.new
+ end
+
+ test "#xpath returns the expanded path as a Pathname object" do
+ assert_equal pn(__FILE__).expand_path, @ph.xpath(__FILE__)
+ end
+
+ test "#normalize_extension returns a bare extension as is" do
+ assert_equal "rb", @ph.normalize_extension("rb")
+ end
+
+ test "#normalize_extension removes a leading dot" do
+ assert_equal "rb", @ph.normalize_extension(".rb")
+ end
+
+ test "#normalize_extension supports symbols" do
+ assert_equal "rb", @ph.normalize_extension(:rb)
+ end
+
+ test "#longest_common_subpath finds the longest common subpath, if there is one" do
+ paths = %w(
+ /foo/bar
+ /foo/baz
+ /foo/bar/baz/woo/zoo
+ ).map { |path| pn(path) }
+
+ assert_equal pn("/foo"), @ph.longest_common_subpath(paths)
+ end
+
+ test "#longest_common_subpath returns the root directory as an edge case" do
+ paths = %w(
+ /foo/bar
+ /foo/baz
+ /foo/bar/baz/woo/zoo
+ /wadus
+ ).map { |path| pn(path) }
+
+ assert_equal pn("/"), @ph.longest_common_subpath(paths)
+ end
+
+ test "#longest_common_subpath returns nil for an empty collection" do
+ assert_nil @ph.longest_common_subpath([])
+ end
+
+ test "#existing_parent returns the most specific existing ascendant" do
+ wd = Pathname.getwd
+
+ assert_equal wd, @ph.existing_parent(wd)
+ assert_equal wd, @ph.existing_parent(wd.join("non-existing/directory"))
+ assert_equal pn("/"), @ph.existing_parent(pn("/non-existing/directory"))
+ end
+
+ test "#filter_out_descendants returns the same collection if there are no descendants (empty)" do
+ assert_equal [], @ph.filter_out_descendants([])
+ end
+
+ test "#filter_out_descendants returns the same collection if there are no descendants (one)" do
+ assert_equal ["/foo"], @ph.filter_out_descendants(["/foo"])
+ end
+
+ test "#filter_out_descendants returns the same collection if there are no descendants (several)" do
+ paths = %w(
+ /Rails.root/app/controllers
+ /Rails.root/app/models
+ /Rails.root/app/helpers
+ ).map { |path| pn(path) }
+
+ assert_equal paths, @ph.filter_out_descendants(paths)
+ end
+
+ test "#filter_out_descendants filters out descendants preserving order" do
+ paths = %w(
+ /Rails.root/app/controllers
+ /Rails.root/app/controllers/concerns
+ /Rails.root/app/models
+ /Rails.root/app/models/concerns
+ /Rails.root/app/helpers
+ ).map { |path| pn(path) }
+
+ assert_equal paths.values_at(0, 2, 4), @ph.filter_out_descendants(paths)
+ end
+
+ test "#filter_out_descendants works on path units" do
+ paths = %w(
+ /foo/bar
+ /foo/barrrr
+ ).map { |path| pn(path) }
+
+ assert_equal paths, @ph.filter_out_descendants(paths)
+ end
+
+ test "#filter_out_descendants deals correctly with the root directory" do
+ paths = %w(
+ /
+ /foo
+ /foo/bar
+ ).map { |path| pn(path) }
+
+ assert_equal paths.values_at(0), @ph.filter_out_descendants(paths)
+ end
+
+ test "#filter_out_descendants preserves duplicates" do
+ paths = %w(
+ /foo
+ /foo/bar
+ /foo
+ ).map { |path| pn(path) }
+
+ assert_equal paths.values_at(0, 2), @ph.filter_out_descendants(paths)
+ end
+end