rspec-path_matchers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.commitlintrc.yml +37 -0
  3. data/.husky/commit-msg +1 -0
  4. data/.release-please-manifest.json +3 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +15 -0
  7. data/.vscode/settings.json +2 -0
  8. data/CHANGELOG.md +43 -0
  9. data/CODE_OF_CONDUCT.md +132 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +409 -0
  12. data/Rakefile +73 -0
  13. data/design.rb +76 -0
  14. data/lib/rspec/path_matchers/matchers/base.rb +197 -0
  15. data/lib/rspec/path_matchers/matchers/directory_contents_inspector.rb +57 -0
  16. data/lib/rspec/path_matchers/matchers/have_directory.rb +126 -0
  17. data/lib/rspec/path_matchers/matchers/have_file.rb +45 -0
  18. data/lib/rspec/path_matchers/matchers/have_no_entry.rb +49 -0
  19. data/lib/rspec/path_matchers/matchers/have_symlink.rb +43 -0
  20. data/lib/rspec/path_matchers/options/atime.rb +51 -0
  21. data/lib/rspec/path_matchers/options/birthtime.rb +60 -0
  22. data/lib/rspec/path_matchers/options/content.rb +59 -0
  23. data/lib/rspec/path_matchers/options/ctime.rb +51 -0
  24. data/lib/rspec/path_matchers/options/group.rb +63 -0
  25. data/lib/rspec/path_matchers/options/json_content.rb +43 -0
  26. data/lib/rspec/path_matchers/options/mode.rb +51 -0
  27. data/lib/rspec/path_matchers/options/mtime.rb +51 -0
  28. data/lib/rspec/path_matchers/options/owner.rb +63 -0
  29. data/lib/rspec/path_matchers/options/size.rb +51 -0
  30. data/lib/rspec/path_matchers/options/symlink_atime.rb +51 -0
  31. data/lib/rspec/path_matchers/options/symlink_birthtime.rb +60 -0
  32. data/lib/rspec/path_matchers/options/symlink_ctime.rb +51 -0
  33. data/lib/rspec/path_matchers/options/symlink_group.rb +63 -0
  34. data/lib/rspec/path_matchers/options/symlink_mtime.rb +51 -0
  35. data/lib/rspec/path_matchers/options/symlink_owner.rb +63 -0
  36. data/lib/rspec/path_matchers/options/symlink_target.rb +54 -0
  37. data/lib/rspec/path_matchers/options/symlink_target_exist.rb +54 -0
  38. data/lib/rspec/path_matchers/options/symlink_target_type.rb +59 -0
  39. data/lib/rspec/path_matchers/options/yaml_content.rb +44 -0
  40. data/lib/rspec/path_matchers/options.rb +33 -0
  41. data/lib/rspec/path_matchers/version.rb +7 -0
  42. data/lib/rspec/path_matchers.rb +33 -0
  43. data/package.json +11 -0
  44. data/release-please-config.json +36 -0
  45. metadata +280 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # atime: <expected>
7
+ class Atime
8
+ def self.key = :atime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the expected value matches the actual value
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.stat(path).atime
29
+ case expected
30
+ when Time, DateTime then match_time(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_time(actual, expected, failure_messages)
38
+ return if expected.to_time == actual
39
+
40
+ failure_messages << "expected #{key} to be #{expected.to_time.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected #{key} to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # birthtime: <expected>
7
+ class Birthtime
8
+ def self.key = :birthtime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the expected birthtime matches the actual birthtime
24
+ # @param path [String] the path of the entry to check
25
+ # @param expected [Object] the expected value to match against the actual value
26
+ # @param failure_messages [Array<String>] the array to append failure messages to
27
+ # @return [Void]
28
+ #
29
+ def self.match(path, expected, failure_messages) # rubocop:disable Metrics/MethodLength
30
+ begin
31
+ actual = File.stat(path).birthtime
32
+ rescue NotImplementedError
33
+ message = "WARNING: #{key} expectations are not supported for #{path} and will be skipped"
34
+ RSpec.configuration.reporter.message(message)
35
+ return
36
+ end
37
+
38
+ case expected
39
+ when Time, DateTime then match_time(actual, expected, failure_messages)
40
+ else match_matcher(actual, expected, failure_messages)
41
+ end
42
+ end
43
+
44
+ # private methods
45
+
46
+ private_class_method def self.match_time(actual, expected, failure_messages)
47
+ return if expected.to_time == actual
48
+
49
+ failure_messages << "expected #{key} to be #{expected.to_time.inspect}, but was #{actual.inspect}"
50
+ end
51
+
52
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
53
+ return if expected.matches?(actual)
54
+
55
+ failure_messages << "expected #{key} to #{expected.description}, but was #{actual.inspect}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # content: <expected>
7
+ class Content
8
+ def self.key = :content
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(String) ||
17
+ expected.is_a?(Regexp) ||
18
+ RSpec::PathMatchers.matcher?(expected)
19
+
20
+ failure_messages <<
21
+ "expected `#{key}:` to be a String, Regexp, or Matcher, but was #{expected.inspect}"
22
+ end
23
+
24
+ # Returns nil if the path matches the expected content
25
+ # @param path [String] the path of the entry to check
26
+ # @return [String, nil]
27
+ #
28
+ def self.match(path, expected, failure_messages)
29
+ actual = File.read(path)
30
+ case expected
31
+ when String then match_string(actual, expected, failure_messages)
32
+ when Regexp then match_regexp(actual, expected, failure_messages)
33
+ else match_matcher(actual, expected, failure_messages)
34
+ end
35
+ end
36
+
37
+ # private methods
38
+
39
+ private_class_method def self.match_string(actual, expected, failure_messages)
40
+ return if expected == actual
41
+
42
+ failure_messages << "expected content to be #{expected.inspect}"
43
+ end
44
+
45
+ private_class_method def self.match_regexp(actual, expected, failure_messages)
46
+ return if expected.match?(actual)
47
+
48
+ failure_messages << "expected content to match #{expected.inspect}"
49
+ end
50
+
51
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
52
+ return if expected.matches?(actual)
53
+
54
+ failure_messages << "expected content to #{expected.description}"
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # ctime: <expected>
7
+ class Ctime
8
+ def self.key = :ctime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path matches the expected size
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.stat(path).ctime
29
+ case expected
30
+ when Time, DateTime then match_time(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_time(actual, expected, failure_messages)
38
+ return if expected.to_time == actual
39
+
40
+ failure_messages << "expected ctime to be #{expected.to_time.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected ctime to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # group: <expected>
7
+ class Group
8
+ def self.key = :group
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(String) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher or a String, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path is owned by the expected owner
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ return if unsupported_platform?
29
+
30
+ actual = Etc.getgrgid(File.stat(path).gid).name
31
+
32
+ case expected
33
+ when String then match_string(actual, expected, failure_messages)
34
+ else match_matcher(actual, expected, failure_messages)
35
+ end
36
+ end
37
+
38
+ # private methods
39
+
40
+ private_class_method def self.unsupported_platform?
41
+ return false if Etc.respond_to?(:getgrgid)
42
+
43
+ # If the platform doesn't support Group checks, warn the user and skip the check
44
+ message = 'WARNING: Group expectations are not supported on this platform and will be skipped.'
45
+ RSpec.configuration.reporter.message(message)
46
+ true
47
+ end
48
+
49
+ private_class_method def self.match_string(actual, expected, failure_messages)
50
+ return if expected == actual
51
+
52
+ failure_messages << "expected group to be #{expected.inspect}, but was #{actual.inspect}"
53
+ end
54
+
55
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
56
+ return if expected.matches?(actual)
57
+
58
+ failure_messages << "expected group to #{expected.description}, but was #{actual.inspect}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module RSpec
6
+ module PathMatchers
7
+ module Options
8
+ # json_content: <expected>
9
+ class JsonContent
10
+ def self.key = :json_content
11
+
12
+ def self.description(expected)
13
+ return 'be json content' if expected == true
14
+
15
+ expected.description
16
+ end
17
+
18
+ def self.validate_expected(expected, failure_messages)
19
+ return if expected == NOT_GIVEN ||
20
+ expected == true ||
21
+ RSpec::PathMatchers.matcher?(expected)
22
+
23
+ failure_messages <<
24
+ "expected `#{key}:` to be a Matcher or true, but was #{expected.inspect}"
25
+ end
26
+
27
+ # Returns nil if the path matches the expected content
28
+ # @param path [String] the path of the entry to check
29
+ # @return [String, nil]
30
+ #
31
+ def self.match(path, expected, failure_messages)
32
+ actual = JSON.parse(File.read(path))
33
+
34
+ return if expected == true
35
+
36
+ failure_messages << "expected JSON content to #{expected.description}" unless expected.matches?(actual)
37
+ rescue JSON::ParserError => e
38
+ failure_messages << "expected valid JSON content, but got error: #{e.message}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # mode: <expected>
7
+ class Mode
8
+ def self.key = :mode
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(String) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher or a String, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path matches the expected mode
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.stat(path).mode.to_s(8)[-4..] # Get the last 4 characters of the octal mode
29
+ case expected
30
+ when String then match_string(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_string(actual, expected, failure_messages)
38
+ return if expected == actual
39
+
40
+ failure_messages << "expected mode to be #{expected.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected mode to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # mtime: <expected>
7
+ class Mtime
8
+ def self.key = :mtime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path matches the expected size
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.stat(path).mtime
29
+ case expected
30
+ when Time, DateTime then match_time(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_time(actual, expected, failure_messages)
38
+ return if expected.to_time == actual
39
+
40
+ failure_messages << "expected mtime to be #{expected.to_time.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected mtime to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # owner: <expected>
7
+ class Owner
8
+ def self.key = :owner
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(String) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher or a String, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path is owned by the expected owner
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ return if unsupported_platform?
29
+
30
+ actual = Etc.getpwuid(File.stat(path).uid).name
31
+
32
+ case expected
33
+ when String then match_string(actual, expected, failure_messages)
34
+ else match_matcher(actual, expected, failure_messages)
35
+ end
36
+ end
37
+
38
+ # private methods
39
+
40
+ private_class_method def self.unsupported_platform?
41
+ return false if Etc.respond_to?(:getpwuid)
42
+
43
+ # If the platform doesn't support ownership, warn the user and skip the check
44
+ message = 'WARNING: Owner expectations are not supported on this platform and will be skipped.'
45
+ RSpec.configuration.reporter.message(message)
46
+ true
47
+ end
48
+
49
+ private_class_method def self.match_string(actual, expected, failure_messages)
50
+ return if expected == actual
51
+
52
+ failure_messages << "expected owner to be #{expected.inspect}, but was #{actual.inspect}"
53
+ end
54
+
55
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
56
+ return if expected.matches?(actual)
57
+
58
+ failure_messages << "expected owner to #{expected.description}, but was #{actual.inspect}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # size: <expected>
7
+ class Size
8
+ def self.key = :size
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.to_s
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Integer) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher or an Integer, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the path matches the expected size
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.size(path)
29
+ case expected
30
+ when Integer then match_integer(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_integer(actual, expected, failure_messages)
38
+ return if expected == actual
39
+
40
+ failure_messages << "expected size to be #{expected.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected size to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # atime: <expected>
7
+ class SymlinkAtime
8
+ def self.key = :atime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the expected value matches the actual value
24
+ # @param path [String] the path of the entry to check
25
+ # @return [String, nil]
26
+ #
27
+ def self.match(path, expected, failure_messages)
28
+ actual = File.lstat(path).atime
29
+ case expected
30
+ when Time, DateTime then match_time(actual, expected, failure_messages)
31
+ else match_matcher(actual, expected, failure_messages)
32
+ end
33
+ end
34
+
35
+ # private methods
36
+
37
+ private_class_method def self.match_time(actual, expected, failure_messages)
38
+ return if expected.to_time == actual
39
+
40
+ failure_messages << "expected #{key} to be #{expected.to_time.inspect}, but was #{actual.inspect}"
41
+ end
42
+
43
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
44
+ return if expected.matches?(actual)
45
+
46
+ failure_messages << "expected #{key} to #{expected.description}, but was #{actual.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module PathMatchers
5
+ module Options
6
+ # birthtime: <expected>
7
+ class SymlinkBirthtime
8
+ def self.key = :birthtime
9
+
10
+ def self.description(expected)
11
+ RSpec::PathMatchers.matcher?(expected) ? expected.description : expected.inspect
12
+ end
13
+
14
+ def self.validate_expected(expected, failure_messages)
15
+ return if expected == NOT_GIVEN ||
16
+ expected.is_a?(Time) || expected.is_a?(DateTime) ||
17
+ RSpec::PathMatchers.matcher?(expected)
18
+
19
+ failure_messages <<
20
+ "expected `#{key}:` to be a Matcher, Time, or DateTime, but was #{expected.inspect}"
21
+ end
22
+
23
+ # Returns nil if the expected birthtime matches the actual birthtime
24
+ # @param path [String] the path of the entry to check
25
+ # @param expected [Object] the expected value to match against the actual value
26
+ # @param failure_messages [Array<String>] the array to append failure messages to
27
+ # @return [Void]
28
+ #
29
+ def self.match(path, expected, failure_messages) # rubocop:disable Metrics/MethodLength
30
+ begin
31
+ actual = File.lstat(path).birthtime
32
+ rescue NotImplementedError
33
+ message = "WARNING: #{key} expectations are not supported for #{path} and will be skipped"
34
+ RSpec.configuration.reporter.message(message)
35
+ return
36
+ end
37
+
38
+ case expected
39
+ when Time, DateTime then match_time(actual, expected, failure_messages)
40
+ else match_matcher(actual, expected, failure_messages)
41
+ end
42
+ end
43
+
44
+ # private methods
45
+
46
+ private_class_method def self.match_time(actual, expected, failure_messages)
47
+ return if expected.to_time == actual
48
+
49
+ failure_messages << "expected #{key} to be #{expected.to_time.inspect}, but was #{actual.inspect}"
50
+ end
51
+
52
+ private_class_method def self.match_matcher(actual, expected, failure_messages)
53
+ return if expected.matches?(actual)
54
+
55
+ failure_messages << "expected #{key} to #{expected.description}, but was #{actual.inspect}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end