minitag 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 683ead8b294111fdcd7f19206a0713fb85bc8ed632aa3a853fcbe72a75abf1d2
4
- data.tar.gz: 3100c71052cd5fc151f0107d76e1abddcc6fe36d1ec649d3d2fc3c385cc5bfa5
3
+ metadata.gz: 8004bb1833fb4829727720b31f79334793e459d8bcb956914bbd6cddb93c85a2
4
+ data.tar.gz: 40271847cdace3dcf99f483d59530b45496fc1a02fc5208e56c6a1f5817c03de
5
5
  SHA512:
6
- metadata.gz: a46ab8f1155605202144a8d6c44a4250409cd0ae94670fe1f748b7c08aacac9c937b9ea97488c7131d00f559be2ea52fc99a5cbb4f430f243e7ddaed0cc01b2d
7
- data.tar.gz: ffa35e8148558a1210e48d92fc3672bae8d46e6eba4f9da49fde5142661af5b90f82d99db56da4d6aa07841b89056ef7012b66da6bbb105ddae64a7a5ea888ae
6
+ metadata.gz: 89b73ed8cdb55e205b3692ebd67c301785ae6e8fb7fcc856c32d0cc863024eb55bbaaa899a439cf1bdb2bef64627f1de7fbc171650dd95b6794ab64f3f6da121
7
+ data.tar.gz: 21be5c61d05d203558b664dbe92a08c01ac809b84580ddbf4ac6a648067e175cebb1c9b7912e91673f14f315f789ae11ee09bf653860b4ed67d53262fecd8db0
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minitag (0.2.0)
4
+ minitag (0.3.0)
5
5
  minitest (~> 5.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -33,6 +33,7 @@ Require `minitag` in our `test_helper.rb`:
33
33
 
34
34
  We can tag specific tests with one or more tags.
35
35
 
36
+ It is important to point out that tags associated with a test have no concept of being inclusive or exclusive. This distinction is only valid for [tag filters](#running-tests-with-tag-filters).
36
37
 
37
38
  ```rb
38
39
  class MyTest < Minitest::Test
@@ -43,15 +44,19 @@ class MyTest < Minitest::Test
43
44
  end
44
45
  ```
45
46
 
46
- ### Running tests
47
+ ### Running tests with tag filters
47
48
 
48
49
  We can now run our test suite with specific tags:
49
50
 
50
- `$ bundle exec rake test --tag 'my_tag'`
51
+ `$ bundle exec rake test --tag 'unit'`
51
52
 
52
- ### Extra
53
+ or even multiple tags:
53
54
 
54
- Tags can be:
55
+ `$ bundle exec rake test --tag 'unit' --tag 'services' --tag '~model'`
56
+
57
+ ### More on tag filters
58
+
59
+ Tag filters can be:
55
60
  1. `inclusive`
56
61
  2. `exclusive` with the `~` prefix:
57
62
 
@@ -2,26 +2,24 @@
2
2
 
3
3
  require 'minitest'
4
4
  require 'minitag/version'
5
- require 'minitag/tag'
6
- require 'minitag/tag_mapper'
7
- require 'minitag/tag_matcher'
5
+ require 'minitag/context'
8
6
  require 'minitag/tag_extension'
9
7
 
10
8
  # Namespace for classes or modules providing tagging functionality
11
9
  # to Minitest::Test
12
10
  module Minitag
13
11
  class << self
14
- # Tags specified by the `--tag` or `-t` option.
15
- def execution_tags
16
- @execution_tags ||= []
12
+ # Execution context of the test suite.
13
+ def context
14
+ @context ||= Context.new
17
15
  end
18
16
 
19
- # Add tag specified by the `--tag` or `-t` option.
20
- def add_execution_tag(tag)
21
- execution_tags << Tag.new(tag)
17
+ # Add filtering tag to context specified by the `--tag` or `-t` option.
18
+ def add_filter(tag)
19
+ context.add_filter(tag)
22
20
  end
23
21
 
24
- # Tags from the last `tag` method waiting to be associated with a test.
22
+ # Tags from the last `tag` method awaiting to be associated with a test.
25
23
  def pending_tags
26
24
  @pending_tags || []
27
25
  end
@@ -30,10 +28,5 @@ module Minitag
30
28
  def pending_tags=(tags)
31
29
  @pending_tags = Array(tags)
32
30
  end
33
-
34
- # The mapping of tags and tests.
35
- def tag_mapping
36
- @tag_mapping ||= Minitag::TagMapper.new
37
- end
38
31
  end
39
32
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative './tag_registry'
5
+
6
+ module Minitag
7
+ # Represents the execution context of the test suite.
8
+ class Context
9
+ def initialize
10
+ @inclusive_filters = Set.new
11
+ @exclusive_filters = Set.new
12
+ @tag_registry = Minitag::TagRegistry.new
13
+ end
14
+
15
+ # Associates tags with a name taking into account its namespace.
16
+ #
17
+ # @param [String] namespace the namespace which a name belongs.
18
+ # @param [String] name the test name.
19
+ # @param [Array] tags the collection of tags.
20
+ #
21
+ # @return [void]
22
+ def add_tags(namespace:, name:, tags:)
23
+ @tag_registry.add(namespace: namespace, name: name, tags: tags)
24
+ end
25
+
26
+ # Adds a filter tag.
27
+ # Tags with a ~ prefix are exclusive filters and inclusive filters otherwise.
28
+ #
29
+ # param [String] name the name of the filter tag.
30
+ #
31
+ # Invariants:
32
+ # - A filter will always be a String without the ~ prefix.
33
+ #
34
+ # @return [void]
35
+ def add_filter(tag)
36
+ if tag.start_with?('~')
37
+ @exclusive_filters << tag[1..-1]
38
+ else
39
+ @inclusive_filters << tag
40
+ end
41
+ end
42
+
43
+ # Indicates when a context has no filters.
44
+ #
45
+ # @return [boolean] whether a context has no filters.
46
+ def no_filters?
47
+ @inclusive_filters.empty? && @exclusive_filters.empty?
48
+ end
49
+
50
+ # Detects whether the name associated with a namespace contains tags
51
+ # that matches the filtering criteria.
52
+ #
53
+ # @param [String] namespace the namespace which a test name belongs.
54
+ # @param [String] name the test name.
55
+ #
56
+ # Invariants:
57
+ # - Returns true when no filters are present.
58
+ #
59
+ # return [boolean] whether there was a match or not.
60
+ def match?(namespace:, name:)
61
+ return true if no_filters?
62
+
63
+ tags = @tag_registry.fetch(namespace: namespace, name: name)
64
+ match_inclusive_filters?(tags) && match_exclusive_filters?(tags)
65
+ end
66
+
67
+ private
68
+
69
+ # Detects whether any of the tags matches the inclusive filters.
70
+ #
71
+ # @param [Set] tags the set of tags.
72
+ #
73
+ # Invariants:
74
+ # - Returns true when no inclusive filters are specified.
75
+ # - Returns false when inclusive filters are specified but there
76
+ # are no tags.
77
+ #
78
+ # return [boolean] whether there was a match or not.
79
+ def match_inclusive_filters?(tags)
80
+ return true if @inclusive_filters.empty?
81
+ return false if @inclusive_filters.any? && tags.empty?
82
+
83
+ (@inclusive_filters & tags).any?
84
+ end
85
+
86
+ # Detects whether any of the tags matches the exclusive filters.
87
+ #
88
+ # @param [Set] tags the set of tags.
89
+ #
90
+ # Invariants:
91
+ # - Returns true when no exclusive filters are specified.
92
+ # - Returns true when exclusive filters are specified and there
93
+ # are no tags.
94
+ #
95
+ # return [boolean] whether there was a match or not.
96
+ def match_exclusive_filters?(tags)
97
+ return true if @exclusive_filters.empty?
98
+ return true if @exclusive_filters.any? && tags.empty?
99
+
100
+ (@exclusive_filters & tags).none?
101
+ end
102
+ end
103
+ end
@@ -7,15 +7,24 @@ module Minitag
7
7
  # - Associate tags with tests
8
8
  # - Filter tests based on the specified tags
9
9
  module TagExtension
10
+ # Add tags to be associated with the next test definition.
11
+ #
12
+ # It is important to notice that tags associated with a test have no concept
13
+ # of inclusive or exclusive tags. This distinction is only valid for tag
14
+ # filters.
15
+ #
16
+ # @param [Array] tags the list of tags to be associated with a test case.
17
+ #
18
+ # return [void]
10
19
  def tag(*tags)
11
- Minitag.pending_tags = tags
20
+ Minitag.pending_tags = tags.map { |tag| tag.to_s.strip.downcase }
12
21
  end
13
22
 
14
23
  define_method(:method_added) do |name|
15
24
  if name[/\Atest_/]
16
- Minitag.pending_tags.each do |pending_tag|
17
- Minitag.tag_mapping.add(context: self, name: name, tag: pending_tag)
18
- end
25
+ Minitag.context.add_tags(
26
+ namespace: self, name: name, tags: Minitag.pending_tags
27
+ )
19
28
 
20
29
  Minitag.pending_tags = []
21
30
  end
@@ -23,17 +32,10 @@ module Minitag
23
32
 
24
33
  def runnable_methods
25
34
  methods = super.dup
26
- return methods if Minitag.execution_tags.empty? || methods.empty?
27
-
28
- execution_tags = Minitag.execution_tags
29
- inclusive_tags = execution_tags.select(&:inclusive?).map(&:name)
30
- exclusive_tags = execution_tags.select(&:exclusive?).map(&:name)
35
+ return methods if Minitag.context.no_filters?
31
36
 
32
37
  methods.select do |runnable_method|
33
- runnable_method_tags = Minitag.tag_mapping.fetch(context: self, name: runnable_method).map(&:name)
34
-
35
- Minitag::TagMatcher.inclusive_match?(inclusive_tags, runnable_method_tags) &&
36
- Minitag::TagMatcher.exclusive_match?(exclusive_tags, runnable_method_tags)
38
+ Minitag.context.match?(namespace: self, name: runnable_method)
37
39
  end
38
40
  end
39
41
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Minitag
6
+ # Stores tags associated with a namespace and name.
7
+ class TagRegistry
8
+ def initialize
9
+ @repository = Hash.new { |h, k| h[k] = Set.new }
10
+ end
11
+
12
+ # Associates tags with a name taking into account its namespace.
13
+ #
14
+ # @param [String] namespace the namespace which a test name belongs.
15
+ # @param [String] name the test name.
16
+ # @param [Array] tags the collection of tags.
17
+ #
18
+ # @return [void]
19
+ def add(namespace:, name:, tags:)
20
+ @repository[key(namespace, name)] = Set.new(tags)
21
+ end
22
+
23
+ # Fetches tags associated with a test name and namespace.
24
+ #
25
+ # @param [String] namespace the namespace which a test name belongs.
26
+ # @param [String] name the test name.
27
+ #
28
+ # @return [Set] the tags associated with the specified namespace and name.
29
+ def fetch(namespace:, name:)
30
+ @repository[key(namespace, name)]
31
+ end
32
+
33
+ private
34
+
35
+ def key(namespace, name)
36
+ "#{namespace}_#{name}"
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minitag
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -8,13 +8,13 @@ module Minitest
8
8
  def self.plugin_minitag_options(opts, options)
9
9
  opts.on '-t', '--tag TAG' do |tag|
10
10
  options[:tags] ||= []
11
- options[:tags] << tag
11
+ options[:tags] << tag.to_s.strip.downcase
12
12
  end
13
13
  end
14
14
 
15
15
  def self.plugin_minitag_init(options)
16
16
  Array(options[:tags]).each do |tag|
17
- Minitag.add_execution_tag(tag.to_s.strip)
17
+ Minitag.context.add_filter(tag)
18
18
  end
19
19
  end
20
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernardo de Araujo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-11 00:00:00.000000000 Z
11
+ date: 2020-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -85,10 +85,9 @@ files:
85
85
  - bin/console
86
86
  - bin/setup
87
87
  - lib/minitag.rb
88
- - lib/minitag/tag.rb
88
+ - lib/minitag/context.rb
89
89
  - lib/minitag/tag_extension.rb
90
- - lib/minitag/tag_mapper.rb
91
- - lib/minitag/tag_matcher.rb
90
+ - lib/minitag/tag_registry.rb
92
91
  - lib/minitag/version.rb
93
92
  - lib/minitest/minitag_plugin.rb
94
93
  - minitag.gemspec
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Minitag
4
- # Represents a tag in our system.
5
- # Provides helper methods to identify the type of a tag, which can
6
- # only one of the following:
7
- # - inclusive
8
- # - exclusive (name with ~ as a prefix)
9
- class Tag
10
- attr_reader :name
11
-
12
- # Initializes a tag. Tags with a ~ prefix are deemed exclusive and
13
- # inclusive otherwise.
14
- #
15
- # param [String] name the name of the tag
16
- #
17
- # Invariants:
18
- # - A tag name will always be a String without the ~ prefix
19
- # after initialization.
20
- def initialize(name)
21
- @name = name.to_s
22
- @exclusive = false
23
-
24
- return unless @name.start_with?('~')
25
-
26
- @name = @name[1..-1]
27
- @exclusive = true
28
- end
29
-
30
- # Whether this tag needs to be excluded or not.
31
- #
32
- # return [boolean]
33
- def exclusive?
34
- @exclusive
35
- end
36
-
37
- # Whether this tag needs to be included or not.
38
- #
39
- # return [boolean]
40
- def inclusive?
41
- !exclusive?
42
- end
43
- end
44
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Minitag
4
- # Stores the mapping between a test name and its existing tags.
5
- class TagMapper
6
- def initialize
7
- @repository = Hash.new { |h, k| h[k] = [] }
8
- end
9
-
10
- # Associates a tag with a test name takinto into account is context.
11
- #
12
- # @param [String] context the context which a test name belongs.
13
- # @param [String] name the test name.
14
- # @param [String] tag the tag name.
15
- #
16
- # @return [void]
17
- def add(context:, name:, tag:)
18
- @repository[key(context, name)] << Minitag::Tag.new(tag)
19
- end
20
-
21
- # Fetches tags associated with a test name and context.
22
- #
23
- # @param [String] context the context which a test name belongs.
24
- # @param [String] name the test name.
25
- #
26
- # @return [Array] the tags associated with the test.
27
- def fetch(context:, name:)
28
- @repository[key(context, name)]
29
- end
30
-
31
- private
32
-
33
- def key(context, name)
34
- "#{context}_#{name}"
35
- end
36
- end
37
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Minitag
4
- # Introduce different match? methods based on the type of the chosen tags.
5
- class TagMatcher
6
- # Detects if any of the desired tags match any of the existing ones.
7
- #
8
- # @param [Array] desired_tags the tags specified by `--tag`.
9
- # @param [Array] existing_tags the tags specified by the `tag` method.
10
- #
11
- # Invariants:
12
- # - Always returns true when no desired tags are specified.
13
- # - Always returns false when desired tags are specified but there are
14
- # no existing tags.
15
- #
16
- # return [boolean] whether there was a match or not.
17
- def self.inclusive_match?(desired_tags, existing_tags)
18
- return true if desired_tags.empty?
19
- return false if desired_tags.any? && existing_tags.empty?
20
-
21
- (desired_tags & existing_tags).any?
22
- end
23
-
24
- # Detects if there are no matches between any of the desired tags
25
- # and existing ones.
26
- #
27
- # @param desired_tags [Array] tags specified by `--tag`.
28
- # @param existing_tags [Array] tags specified by the `tag` method.
29
- #
30
- # Invariants:
31
- # - Always returns true when no desired tags are specified.
32
- # - Always returns true when desired tags are specified but there are
33
- # no existing tags.
34
- #
35
- # return [boolean] whether there was no match or not.
36
- def self.exclusive_match?(desired_tags, existing_tags)
37
- return true if desired_tags.empty?
38
- return true if desired_tags.any? && existing_tags.empty?
39
-
40
- (desired_tags & existing_tags).none?
41
- end
42
- end
43
- end