flutter 0.1.0.pre.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdffa233de57141c16bbbd41684ca85b04208b32edeec97625662383f4e3e753
4
- data.tar.gz: be3eb7f6bb1d0ffd8123ad4b8c2293ecdaf4c3979c75be8471542a2c26e1d771
3
+ metadata.gz: 2ca2f5eb98359bf0cbac8abbdedcbe6ca27ec7bf205625f44cf0c4150313eeb4
4
+ data.tar.gz: 6620320c3c54d63117ddc95550c0d4223411bfe9b6b469336fe9f60ffcddcfe3
5
5
  SHA512:
6
- metadata.gz: e301754a8492f5eaae1ba2388fe39c4d1fe2250b61bb7b792025d646e96719bd1df76a1ed8f669a821107e4e12f54b89c070671f5b86c29d24eae7bf2a3de3d7
7
- data.tar.gz: 4e69808a87b89d50f3d7fcf964ae3a2d5ccc176294e7f610e00d5ffb712131384ff24bb64e630e1ef1f5df1afac7f2175c986d031b4ada8dba9af37df00c930d
6
+ metadata.gz: 6f97e5720ea6fd890e4c6283a1193750eea3c1ffa64de2de550793cecb14f8636a205774a08811a3f4aea35ed5a764023603fa91a33bd0b4d00496925f4ab35c
7
+ data.tar.gz: 7f890be9219df6f89fde38e9bc34479a65c9a3d8d910ec1495732a2a32ac65d33291c507b8e7b6950facca0d02b6a47c726c864ae2abde9a4416ae983096b29c
data/CHANGELOG.md CHANGED
@@ -4,17 +4,27 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased]
8
- ## [0.1.0.pre.3]
7
+ ## Unreleased
8
+ ## 0.2.0
9
9
  ### Added
10
- - Improved documentation for Tracker
11
- - Improved documentation for Persistance classes
10
+ - CI Recipe in README
11
+ - CI integration for incremental tests in branches/pull requests
12
+ - Release workflow tasks & github actions integration
13
+
14
+ ### Fixed
15
+ - Ensure all methods (including inherited ones) are considered when calculating signatures for a class or module.
16
+ - Fix calculation of total / filtered examples for rspec integration
17
+
18
+ ## 0.1.0.pre.3
19
+ ### Added
20
+ - Improved documentation for Persistence classes
21
+ - Improved documentation for Tracker
12
22
 
13
23
  ### Changed
14
- - Pinned dependencies to known working minimum versions
24
+ - Pinned dependencies to known working minimum versions
15
25
 
16
- ## [0.1.0.pre.2]
26
+ ## 0.1.0.pre.2
17
27
  ### Added
18
- - Minitest integration
19
- - RSpec integration
28
+ - Minitest integration
29
+ - RSpec integration
20
30
 
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group :test, :development do
11
11
  gem "overcommit", "~> 0.59.1"
12
12
  gem "gem-release", "~> 2.2"
13
13
  gem "guard", "~> 2.18"
14
+ gem "keepachangelog", "~> 0.6.1"
14
15
  gem "rubocop", "~> 1.21"
15
16
  gem "rubocop-shopify", require: false
16
17
  gem "rubocop-minitest", "~> 0.22.1"
data/README.md CHANGED
@@ -107,7 +107,55 @@ guard :rspec, cmd: "rspec" do
107
107
  end
108
108
  ```
109
109
  ## Configuring flutter in continuous integration
110
- **TODO**
110
+
111
+ Flutter can be used in continuous integration environments to speed up the turn
112
+ around time from running tests by only running tests affected by the changes
113
+ in a pull request.
114
+
115
+ ### Github Actions
116
+ The following example workflow with github actions does the following:
117
+ - Always run all tests on the `main` branch
118
+ - Only run tests affected by the "current" commit for CI workflows triggered by a `push` event on other branches
119
+ - If the CI workflow is triggered due to a `pull_request` event, run all tests affected by all commits in the branch
120
+ (by comparing against the branch point of the pull request)
121
+
122
+ ```yaml
123
+ # Get the commit where this branch diverges from origin/main
124
+ - name: Retrieve branch point
125
+ if: github.event_name == 'pull_request'
126
+ run: |
127
+ echo "::set-output name=KEY::$(diff -u <(git rev-list --first-parent origin/main) <(git rev-list --first-parent HEAD) | sed -ne 's/^ //p' | head -1)"
128
+ id: cache_keys
129
+ # Use the always-upload-cache action to:
130
+ # - Restore the flutter state from cache from either the branch point (if it was set in the previous step)
131
+ # or the last run in the current branch
132
+ # - After the run cache the flutter state using the current commit hash as the hash key
133
+ - name: Setup flutter state
134
+ id: flutter-state
135
+ uses: pat-s/always-upload-cache@v2.1.5
136
+ env:
137
+ cache-name: cache-flutter-state
138
+ with:
139
+ path: .flutter
140
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ github.sha }}
141
+ restore-keys: |
142
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ steps.cache_keys.outputs.KEY }}
143
+ # If this is a push event on the main branch, clear the flutter state
144
+ # so that all tests are run and a full state is cached on the main branch
145
+ - name: Clear flutter state
146
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/main')
147
+ run: rm -rf .flutter
148
+ ```
149
+ > **Note**
150
+ > The exact CI configuration would ofcourse depend on your workflow and confidence in selectively
151
+ > running tests for pull requests.
152
+
153
+ > **Warning**
154
+ > Selectively running tests in a pull request would show a drop in coverage if you are collecting
155
+ > and/or using code coverage as a "Check". One way to make Flutter work hand in hand with code
156
+ > coverage checks is to only validate that the diff in the pull request has a 100% coverage. For
157
+ > example with [codecov](https://docs.codecov.com/docs/commit-status#section-project-status) this can be
158
+ > achieved by only enabling the `project` status for the main branch and `patch` status otherwise.
111
159
 
112
160
  ## Related work
113
161
 
@@ -119,9 +167,19 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
119
167
 
120
168
  This project uses [overcommit](https://github.com/sds/overcommit) to enforce standards. Enable the precommit hooks in your local checkout by running: `overcommit --sign`
121
169
 
122
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number by running `gem bump`,
123
- then move the `Unreleased` entries in the [CHANGELOG](./CHANGELOG.md) to the new version and commit that.
124
- Finally tag and release the gem with `gem tag` and `gem release`
170
+ To install this gem onto your local machine, run `bundle exec rake install`.
171
+
172
+ ### Releasing a new version
173
+ - Ensure that the [Unreleased](./CHANGELOG.md#Unreleased) section of the changelog is up to date
174
+ and contains useful details.
175
+ - Create a new release using the `release` rake task as follows (for more details about specifying the version change
176
+ run `gem bump --help` which is the command used by the task):
177
+ - Patch release `bundle exec rake release["-v patch"]`
178
+ - Minor release `bundle exec rake release["-v minor"]`
179
+ - Major release `bundle exec rake release["-v major"]`
180
+ > **Note**
181
+ > The `release` rake task automates updating the changelog & version, committing the changes & creating a new tag
182
+ - Push the tag. The CI workflow for tag pushes will take care of publishing the gem & creating a github release.
125
183
 
126
184
  ## Contributing
127
185
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
3
  require "rake/testtask"
4
+ require "keepachangelog"
5
5
 
6
6
  Rake::TestTask.new(:unit) do |t|
7
7
  t.libs << "test"
@@ -22,3 +22,37 @@ RSpec::Core::RakeTask.new(:spec)
22
22
 
23
23
  task test: [:unit, :spec]
24
24
  task default: [:test, :spec, "rubocop:autocorrect_all"]
25
+
26
+ desc "Increment the version, update changelog and create a tag for the release"
27
+ task :release, [:version] do |_t, args|
28
+ parser = Keepachangelog::MarkdownParser.load("CHANGELOG.md")
29
+ log = parser.parsed_content["versions"].delete("Unreleased")
30
+ sh("gem bump --pretend #{args[:version]}") do |ok, _|
31
+ if ok
32
+ new_version = %x(gem bump --no-commit #{args[:version]} | awk '{print $4}' | uniq).chomp
33
+ parser.parsed_content["versions"]["Unreleased"] = { "url" => nil, "date" => nil, "changes" => {} }
34
+ parser.parsed_content["versions"][new_version] = log
35
+ File.open("CHANGELOG.md", "w") do |file|
36
+ file.write(parser.to_md)
37
+ end
38
+ %x(git add CHANGELOG.md lib/flutter/version.rb)
39
+ %x(git commit -m "Bump flutter to #{new_version}")
40
+ %x(gem tag -s)
41
+ Rake::Task["release_notes"].execute({ version: new_version })
42
+ end
43
+ end
44
+ end
45
+
46
+ desc "Get release notes for the current or specific version"
47
+ task :release_notes, [:version] do |_t, args|
48
+ version = (args[:version] || Flutter::VERSION)
49
+ parser = Keepachangelog::MarkdownParser.load("CHANGELOG.md")
50
+ parser.parsed_content.delete("intro")
51
+ parser.parsed_content.delete("title")
52
+ parser.parsed_content["versions"] = parser.parsed_content["versions"].select { |k, _v| k == version }
53
+ lines = parser.to_md.split("\n")
54
+ chunk = ["## #{version}"] + (
55
+ lines.slice_after { |line| line.include?("## #{version}") }.to_a[1] || []
56
+ )
57
+ puts chunk.join("\n")
58
+ end
data/codecov.yml ADDED
@@ -0,0 +1,13 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ paths:
6
+ - "lib"
7
+ branches:
8
+ - main
9
+ patch:
10
+ default:
11
+ paths:
12
+ - "lib"
13
+ informational: false
@@ -10,7 +10,7 @@ end
10
10
  module Flutter
11
11
  module Minitest
12
12
  class << self
13
- attr_accessor :filtered
13
+ attr_accessor :filtered, :total
14
14
 
15
15
  def flutter_tracker
16
16
  @tracker ||= Flutter::Tracker.new(
@@ -31,7 +31,7 @@ module Flutter
31
31
  return unless ::Flutter.enabled
32
32
 
33
33
  Flutter::Minitest.flutter_tracker.persist!
34
- $stdout.puts "Flutter filtered out #{Flutter::Minitest.filtered} tests"
34
+ $stdout.puts "Flutter filtered #{Flutter::Minitest.filtered} / #{Flutter::Minitest.total} tests"
35
35
  if @verbose
36
36
  $stdout.puts "Persisted flutter #{Flutter::Minitest.flutter_tracker}"
37
37
  end
@@ -43,17 +43,15 @@ module Flutter
43
43
  module ClassMethods
44
44
  def runnable_methods
45
45
  Flutter::Minitest.filtered ||= 0
46
+ Flutter::Minitest.total ||= 0
46
47
  default = super()
48
+ Flutter::Minitest.total += default.length
47
49
  default.select do |test|
48
- skip = Minitest.flutter_tracker.skip?(
50
+ !Minitest.flutter_tracker.skip?(
49
51
  "#{name}##{test}",
50
52
  File.absolute_path(instance_method(test).source_location[0]),
51
53
  instance_method(test).source,
52
- )
53
- if skip
54
- Flutter::Minitest.filtered += 1
55
- end
56
- !skip
54
+ ).tap { |skip| Flutter::Minitest.filtered += 1 if skip }
57
55
  end
58
56
  end
59
57
  end
@@ -8,6 +8,11 @@ module Flutter
8
8
  class Parser
9
9
  attr_reader :signatures
10
10
 
11
+ @method_cache = {}
12
+ class << self
13
+ attr_reader :method_cache
14
+ end
15
+
11
16
  def initialize(file)
12
17
  @signatures = {}
13
18
  @targets = Set.new
@@ -49,23 +54,17 @@ module Flutter
49
54
  require_relative @file
50
55
  @targets.each do |container|
51
56
  instance = Kernel.const_get(container)
52
- class_methods = (
53
- instance.methods - Object.methods
54
- ) + (
55
- instance.private_methods - Object.private_methods
56
- )
57
- instance_methods = (
58
- instance.instance_methods - Object.instance_methods
59
- ) + (
60
- instance.private_instance_methods - Object.private_instance_methods
61
- )
57
+ class_methods = instance.methods + instance.private_methods
58
+ instance_methods = instance.instance_methods + instance.private_instance_methods
62
59
 
63
60
  @signatures.merge!(class_methods.map do |method|
64
- ["#{container}::#{method}", source_hash(instance.method(method))]
65
- end.to_h)
61
+ hash = source_hash(instance.method(method))
62
+ ["#{container}::#{method}", hash] if hash
63
+ end.compact.to_h)
66
64
  @signatures.merge!(instance_methods.map do |method|
67
- ["#{container}:#{method}", source_hash(instance.instance_method(method))]
68
- end.to_h)
65
+ hash = source_hash(instance.instance_method(method))
66
+ ["#{container}:#{method}", hash] if hash
67
+ end.compact.to_h)
69
68
  rescue NameError
70
69
  $stderr.puts "failed to load #{container} in #{@file}"
71
70
  end
@@ -74,9 +73,12 @@ module Flutter
74
73
  end
75
74
 
76
75
  def source_hash(callable)
77
- Digest::SHA1.hexdigest(callable.source)
76
+ return unless callable.source_location
77
+
78
+ sep = callable.is_a?(UnboundMethod) ? ":" : "::"
79
+ Parser.method_cache["#{callable.owner}#{sep}#{callable.name}"] ||= Digest::SHA1.hexdigest(callable.source)
78
80
  rescue MethodSource::SourceNotFoundError
79
- "<no-source>"
81
+ nil
80
82
  end
81
83
  end
82
84
  end
data/lib/flutter/rspec.rb CHANGED
@@ -5,7 +5,7 @@ require_relative "tracker"
5
5
  module Flutter
6
6
  module RSpec
7
7
  class << self
8
- attr_accessor :filtered
8
+ attr_accessor :filtered, :total
9
9
 
10
10
  def tracker
11
11
  @tracker ||= Flutter::Tracker.new(
@@ -17,20 +17,20 @@ module Flutter
17
17
 
18
18
  module ClassMethods
19
19
  def filtered_examples
20
- Flutter::RSpec.filtered ||= 0
20
+ Flutter::RSpec.filtered ||= Set.new
21
+ Flutter::RSpec.total ||= Set.new
21
22
  Flutter::RSpec.tracker.reset! if Flutter.enabled && Flutter.config.reset_storage
22
23
 
23
24
  original = super
25
+ Flutter::RSpec.total.merge(original)
24
26
  return original unless Flutter.enabled
25
27
 
26
28
  original.select do |example|
27
- skip = example.metadata[:block] && Flutter::RSpec.tracker.skip?(
29
+ !(example.metadata[:block] && Flutter::RSpec.tracker.skip?(
28
30
  example.full_description,
29
31
  example.metadata[:absolute_file_path],
30
32
  example.metadata[:block].source,
31
- )
32
- Flutter::RSpec.filtered += 1 if skip
33
- !skip
33
+ ).tap { |skip| Flutter::RSpec.filtered << example if skip })
34
34
  end
35
35
  end
36
36
  end
@@ -58,7 +58,7 @@ if defined?(RSpec.configure)
58
58
  config.after(:suite) do
59
59
  if Flutter.enabled
60
60
  $stdout.puts
61
- $stdout.puts "Flutter filtered out #{Flutter::RSpec.filtered} examples"
61
+ $stdout.puts "Flutter filtered #{Flutter::RSpec.filtered.length} / #{Flutter::RSpec.total.length} examples"
62
62
  Flutter::RSpec.tracker.persist!
63
63
  end
64
64
  end
@@ -98,6 +98,7 @@ module Flutter
98
98
  # that it was configured with
99
99
  # @return [void]
100
100
  def reset!
101
+ $stdout.puts "Resetting flutter: #{@storage}"
101
102
  @storage.clear!
102
103
  @source_mapping.clear
103
104
  @test_mapping.clear
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Flutter
4
- VERSION = "0.1.0.pre.3"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flutter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ali-Akber Saifee
8
8
  - Ankita Gupta
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-09-30 00:00:00.000000000 Z
12
+ date: 2022-10-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: deep_merge
@@ -73,6 +73,7 @@ files:
73
73
  - README.md
74
74
  - Rakefile
75
75
  - TODO.md
76
+ - codecov.yml
76
77
  - lib/flutter.rb
77
78
  - lib/flutter/config.rb
78
79
  - lib/flutter/minitest.rb
@@ -90,7 +91,7 @@ metadata:
90
91
  homepage_uri: https://github.com/indydevs/flutter
91
92
  source_code_uri: https://github.com/indydevs/flutter
92
93
  changelog_uri: https://github.com/indydevs/flutter/blob/master/CHANGELOG.md
93
- post_install_message:
94
+ post_install_message:
94
95
  rdoc_options: []
95
96
  require_paths:
96
97
  - lib
@@ -101,12 +102,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
102
  version: 2.7.0
102
103
  required_rubygems_version: !ruby/object:Gem::Requirement
103
104
  requirements:
104
- - - ">"
105
+ - - ">="
105
106
  - !ruby/object:Gem::Version
106
- version: 1.3.1
107
+ version: '0'
107
108
  requirements: []
108
- rubygems_version: 3.2.33
109
- signing_key:
109
+ rubygems_version: 3.3.7
110
+ signing_key:
110
111
  specification_version: 4
111
112
  summary: Intelligent test selection based on incremental code changes
112
113
  test_files: []