From 4b0e950736b7394f9b80c3780c4881d6dfa9fd2f Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Mon, 25 Mar 2024 10:55:40 -0400 Subject: [PATCH] utils/path: add child_of? method --- Library/Homebrew/test/utils/path_spec.rb | 33 ++++++++++++++++++++++++ Library/Homebrew/utils/path.rb | 14 ++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Library/Homebrew/test/utils/path_spec.rb create mode 100644 Library/Homebrew/utils/path.rb diff --git a/Library/Homebrew/test/utils/path_spec.rb b/Library/Homebrew/test/utils/path_spec.rb new file mode 100644 index 0000000000..ba7f27436c --- /dev/null +++ b/Library/Homebrew/test/utils/path_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "utils/path" + +RSpec.describe Utils::Path do + describe "::child_of?" do + it "recognizes a path as its own child" do + expect(described_class.child_of?("/foo/bar", "/foo/bar")).to be(true) + end + + it "recognizes a path that is a child of the parent" do + expect(described_class.child_of?("/foo", "/foo/bar")).to be(true) + end + + it "recognizes a path that is a grandchild of the parent" do + expect(described_class.child_of?("/foo", "/foo/bar/baz")).to be(true) + end + + it "does not recognize a path that is not a child" do + expect(described_class.child_of?("/foo", "/bar/baz")).to be(false) + end + + it "handles . and .. in paths correctly" do + expect(described_class.child_of?("/foo", "/foo/./bar")).to be(true) + expect(described_class.child_of?("/foo/bar", "/foo/../foo/bar/baz")).to be(true) + end + + it "handles relative paths correctly" do + expect(described_class.child_of?("foo", "./bar/baz")).to be(false) + expect(described_class.child_of?("../foo", "./bar/baz/../../../foo/bar/baz")).to be(true) + end + end +end diff --git a/Library/Homebrew/utils/path.rb b/Library/Homebrew/utils/path.rb new file mode 100644 index 0000000000..b2f69504f6 --- /dev/null +++ b/Library/Homebrew/utils/path.rb @@ -0,0 +1,14 @@ +# typed: strict +# frozen_string_literal: true + +module Utils + module Path + sig { params(parent: T.any(Pathname, String), child: T.any(Pathname, String)).returns(T::Boolean) } + def self.child_of?(parent, child) + parent_pathname = Pathname(parent).expand_path + child_pathname = Pathname(child).expand_path + child_pathname.ascend { |p| return true if p == parent_pathname } + false + end + end +end