rspec-path_matchers 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +21 -0
- data/README.md +161 -260
- data/design.rb +10 -10
- data/lib/rspec/path_matchers/matchers/base.rb +210 -56
- data/lib/rspec/path_matchers/matchers/directory_matcher.rb +172 -0
- data/lib/rspec/path_matchers/matchers/{have_file.rb → file_matcher.rb} +8 -5
- data/lib/rspec/path_matchers/matchers/no_entry_matcher.rb +64 -0
- data/lib/rspec/path_matchers/matchers/{have_symlink.rb → symlink_matcher.rb} +9 -6
- data/lib/rspec/path_matchers/options/atime.rb +6 -40
- data/lib/rspec/path_matchers/options/base.rb +296 -0
- data/lib/rspec/path_matchers/options/birthtime.rb +6 -49
- data/lib/rspec/path_matchers/options/content.rb +13 -42
- data/lib/rspec/path_matchers/options/ctime.rb +6 -40
- data/lib/rspec/path_matchers/options/etc_base.rb +42 -0
- data/lib/rspec/path_matchers/options/file_stat_base.rb +47 -0
- data/lib/rspec/path_matchers/options/group.rb +5 -52
- data/lib/rspec/path_matchers/options/json_content.rb +6 -30
- data/lib/rspec/path_matchers/options/mode.rb +6 -40
- data/lib/rspec/path_matchers/options/mtime.rb +7 -41
- data/lib/rspec/path_matchers/options/owner.rb +6 -53
- data/lib/rspec/path_matchers/options/parsed_content_base.rb +67 -0
- data/lib/rspec/path_matchers/options/size.rb +5 -40
- data/lib/rspec/path_matchers/options/symlink_atime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_birthtime.rb +7 -49
- data/lib/rspec/path_matchers/options/symlink_ctime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_group.rb +6 -52
- data/lib/rspec/path_matchers/options/symlink_mtime.rb +7 -40
- data/lib/rspec/path_matchers/options/symlink_owner.rb +7 -53
- data/lib/rspec/path_matchers/options/symlink_target.rb +6 -43
- data/lib/rspec/path_matchers/options/symlink_target_exist.rb +6 -41
- data/lib/rspec/path_matchers/options/symlink_target_type.rb +20 -43
- data/lib/rspec/path_matchers/options/yaml_content.rb +6 -31
- data/lib/rspec/path_matchers/options.rb +1 -0
- data/lib/rspec/path_matchers/refinements.rb +79 -0
- data/lib/rspec/path_matchers/version.rb +1 -1
- data/lib/rspec/path_matchers.rb +185 -16
- metadata +12 -8
- data/lib/rspec/path_matchers/matchers/directory_contents_inspector.rb +0 -57
- data/lib/rspec/path_matchers/matchers/have_directory.rb +0 -126
- data/lib/rspec/path_matchers/matchers/have_no_entry.rb +0 -49
@@ -1,62 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'etc_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# group: <expected>
|
7
|
-
class Group
|
9
|
+
class Group < EtcBase
|
8
10
|
def self.key = :group
|
9
|
-
|
10
|
-
def self.
|
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
|
11
|
+
def self.stat_attribute = :gid
|
12
|
+
def self.etc_method = :getgrgid
|
60
13
|
end
|
61
14
|
end
|
62
15
|
end
|
@@ -2,41 +2,17 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
|
5
|
+
require_relative 'parsed_content_base'
|
6
|
+
|
5
7
|
module RSpec
|
6
8
|
module PathMatchers
|
7
9
|
module Options
|
8
10
|
# json_content: <expected>
|
9
|
-
class JsonContent
|
11
|
+
class JsonContent < ParsedContentBase
|
10
12
|
def self.key = :json_content
|
11
|
-
|
12
|
-
def self.
|
13
|
-
|
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
|
13
|
+
private_class_method def self.content_type = 'JSON'
|
14
|
+
private_class_method def self.parse(string) = JSON.parse(string)
|
15
|
+
private_class_method def self.parsing_error = JSON::ParserError
|
40
16
|
end
|
41
17
|
end
|
42
18
|
end
|
@@ -1,50 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# mode: <expected>
|
7
|
-
class Mode
|
9
|
+
class Mode < FileStatBase
|
8
10
|
def self.key = :mode
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
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
|
11
|
+
def self.stat_attribute = :mode
|
12
|
+
def self.valid_expected_types = [String]
|
13
|
+
def self.fetch_actual(path, _failures) = File.stat(path).mode.to_s(8)[-4..]
|
48
14
|
end
|
49
15
|
end
|
50
16
|
end
|
@@ -1,50 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
|
-
#
|
7
|
-
class Mtime
|
8
|
+
# Inherits all logic from FileStatOption
|
9
|
+
class Mtime < FileStatBase
|
8
10
|
def self.key = :mtime
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
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
|
11
|
+
def self.stat_attribute = :mtime
|
12
|
+
def self.valid_expected_types = [Time, DateTime]
|
13
|
+
def self.normalize_expected_literal(expected) = expected.to_time
|
48
14
|
end
|
49
15
|
end
|
50
16
|
end
|
@@ -1,62 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'etc_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
|
-
#
|
7
|
-
class Owner
|
8
|
+
# group: <expected>
|
9
|
+
class Owner < EtcBase
|
8
10
|
def self.key = :owner
|
9
|
-
|
10
|
-
def self.
|
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
|
11
|
+
def self.stat_attribute = :uid
|
12
|
+
def self.etc_method = :getpwuid
|
60
13
|
end
|
61
14
|
end
|
62
15
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module RSpec
|
6
|
+
module PathMatchers
|
7
|
+
module Options
|
8
|
+
# Base class for options that parse file content like JSON, and YAML
|
9
|
+
class ParsedContentBase < Base
|
10
|
+
def self.valid_expected_types = [TrueClass]
|
11
|
+
|
12
|
+
# Reads and parses the file content
|
13
|
+
#
|
14
|
+
# This method will rescue any parsing errors (e.g., `JSON::ParserError`)
|
15
|
+
# and add a descriptive failure instead of crashing.
|
16
|
+
#
|
17
|
+
# @param (see RSpec::PathMatchers::Options::Base.fetch_actual)
|
18
|
+
#
|
19
|
+
# @return [Object, FETCH_ERROR] The parsed content (e.g., a Hash) or FETCH_ERROR.
|
20
|
+
#
|
21
|
+
def self.fetch_actual(path, failures)
|
22
|
+
parse(File.read(path))
|
23
|
+
rescue parsing_error => e
|
24
|
+
message = "expected valid #{content_type} content, but got error: #{e.message}"
|
25
|
+
add_failure(message, failures)
|
26
|
+
FETCH_ERROR
|
27
|
+
end
|
28
|
+
|
29
|
+
# This is the `xxxx_content: true` case. A successful fetch_actual is sufficient
|
30
|
+
def self.match_literal(_actual, _expected, _failures); end
|
31
|
+
|
32
|
+
# Compares the parsed content against the given RSpec matcher.
|
33
|
+
#
|
34
|
+
# @param (see RSpec::PathMatchers::Options::Base.match_matcher)
|
35
|
+
#
|
36
|
+
# @return [void]
|
37
|
+
#
|
38
|
+
def self.match_matcher(actual, expected, failures)
|
39
|
+
return if expected.matches?(actual)
|
40
|
+
|
41
|
+
message = "expected #{content_type} content to #{expected.description}"
|
42
|
+
add_failure(message, failures)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Provides a human-readable description for the option
|
46
|
+
#
|
47
|
+
# Returns a special message for the `json_content: true` case.
|
48
|
+
#
|
49
|
+
def self.description(expected)
|
50
|
+
expected == true ? "be #{content_type.downcase} content" : super
|
51
|
+
end
|
52
|
+
|
53
|
+
private_class_method def self.content_type
|
54
|
+
raise NotImplementedError, 'Subclasses must implement ParsedContentBase.content_type'
|
55
|
+
end
|
56
|
+
|
57
|
+
private_class_method def self.parse(string)
|
58
|
+
raise NotImplementedError, 'Subclasses must implement ParsedContentBase.parse'
|
59
|
+
end
|
60
|
+
|
61
|
+
private_class_method def self.parsing_error
|
62
|
+
raise NotImplementedError, 'Subclasses must implement ParsedContentBase.parsing_error'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,50 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# size: <expected>
|
7
|
-
class Size
|
9
|
+
class Size < FileStatBase
|
8
10
|
def self.key = :size
|
9
|
-
|
10
|
-
def self.
|
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
|
11
|
+
def self.stat_attribute = :size
|
12
|
+
def self.valid_expected_types = [Integer]
|
48
13
|
end
|
49
14
|
end
|
50
15
|
end
|
@@ -1,50 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# atime: <expected>
|
7
|
-
class SymlinkAtime
|
9
|
+
class SymlinkAtime < FileStatBase
|
8
10
|
def self.key = :atime
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
12
|
-
|
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
|
11
|
+
def self.stat_attribute = :atime
|
12
|
+
def self.valid_expected_types = [Time, DateTime]
|
13
|
+
def self.normalize_expected_literal(expected) = expected.to_time
|
14
|
+
def self.stat_source_method = :lstat
|
48
15
|
end
|
49
16
|
end
|
50
17
|
end
|
@@ -1,59 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# birthtime: <expected>
|
7
|
-
class SymlinkBirthtime
|
9
|
+
class SymlinkBirthtime < FileStatBase
|
8
10
|
def self.key = :birthtime
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
12
|
-
|
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
|
11
|
+
def self.stat_attribute = :birthtime
|
12
|
+
def self.valid_expected_types = [Time, DateTime]
|
13
|
+
def self.normalize_expected_literal(expected) = expected.to_time
|
14
|
+
def self.stat_source_method = :lstat
|
57
15
|
end
|
58
16
|
end
|
59
17
|
end
|
@@ -1,50 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'file_stat_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# ctime: <expected>
|
7
|
-
class SymlinkCtime
|
9
|
+
class SymlinkCtime < FileStatBase
|
8
10
|
def self.key = :ctime
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
12
|
-
|
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.lstat(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
|
11
|
+
def self.stat_attribute = :ctime
|
12
|
+
def self.valid_expected_types = [Time, DateTime]
|
13
|
+
def self.normalize_expected_literal(expected) = expected.to_time
|
14
|
+
def self.stat_source_method = :lstat
|
48
15
|
end
|
49
16
|
end
|
50
17
|
end
|
@@ -1,62 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'etc_base'
|
4
|
+
|
3
5
|
module RSpec
|
4
6
|
module PathMatchers
|
5
7
|
module Options
|
6
8
|
# group: <expected>
|
7
|
-
class SymlinkGroup
|
9
|
+
class SymlinkGroup < EtcBase
|
8
10
|
def self.key = :group
|
9
|
-
|
10
|
-
def self.
|
11
|
-
|
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 group
|
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.lstat(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
|
11
|
+
def self.stat_source_method = :lstat
|
12
|
+
def self.stat_attribute = :gid
|
13
|
+
def self.etc_method = :getgrgid
|
60
14
|
end
|
61
15
|
end
|
62
16
|
end
|