flutter 0.1.0.pre.3 → 0.2.1

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: cbd8d330334676bcfcb2d328466c569bd561ed05a7fd3a45e880cace5670af56
4
+ data.tar.gz: 5b4fcd28bb14fa1a6fd5b9c834abbd5bf368c56e7496b574864f2f443d7effb5
5
5
  SHA512:
6
- metadata.gz: e301754a8492f5eaae1ba2388fe39c4d1fe2250b61bb7b792025d646e96719bd1df76a1ed8f669a821107e4e12f54b89c070671f5b86c29d24eae7bf2a3de3d7
7
- data.tar.gz: 4e69808a87b89d50f3d7fcf964ae3a2d5ccc176294e7f610e00d5ffb712131384ff24bb64e630e1ef1f5df1afac7f2175c986d031b4ada8dba9af37df00c930d
6
+ metadata.gz: e6720c70a784f3b6862d7bc07749465fbf5834fb1c6b7fe19b76caa7c0fd52bec2e0b79fc9100385dcc878881ec77199f7e8ff34e0105f5d272b806eeb12ef06
7
+ data.tar.gz: 4cd2b8a88705a44a6b1f28159e01bc8f0e2aed6f54caf287617d81a2ebd823ddcc02ec0867110f7d38da4c5b3541d491cb366e82fd9d94bad319ca7588c74d6d
data/CHANGELOG.md CHANGED
@@ -4,17 +4,30 @@ 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
+ ## Fixed
9
+ - Corrected integration examples for guard in README
10
+ ## 0.2.1
11
+ ## 0.2.0
9
12
  ### Added
10
- - Improved documentation for Tracker
11
- - Improved documentation for Persistance classes
13
+ - CI Recipe in README
14
+ - CI integration for incremental tests in branches/pull requests
15
+ - Release workflow tasks & github actions integration
16
+
17
+ ### Fixed
18
+ - Ensure all methods (including inherited ones) are considered when calculating signatures for a class or module.
19
+ - Fix calculation of total / filtered examples for rspec integration
20
+
21
+ ## 0.1.0.pre.3
22
+ ### Added
23
+ - Improved documentation for Persistence classes
24
+ - Improved documentation for Tracker
12
25
 
13
26
  ### Changed
14
- - Pinned dependencies to known working minimum versions
27
+ - Pinned dependencies to known working minimum versions
15
28
 
16
- ## [0.1.0.pre.2]
29
+ ## 0.1.0.pre.2
17
30
  ### Added
18
- - Minitest integration
19
- - RSpec integration
31
+ - Minitest integration
32
+ - RSpec integration
20
33
 
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/Guardfile CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  guard :minitest, test_folders: ["test"] do
4
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { "test" }
4
+ watch(%r{^(test|lib)/(.*/)?([^/]+)\.rb$}) { "test" }
5
5
  end
6
6
 
7
- guard :rspec, cmd: "rspec" do
8
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { "spec" }
7
+ guard :rspec, cmd: "rspec", all_on_start: true do
8
+ watch(%r{^(spec|lib)/(.*/)?([^/]+)\.rb$}) { "spec" }
9
9
  end
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![CI](https://github.com/indydevs/flutter/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/indydevs/flutter/actions/workflows/main.yml)
4
4
  [![codecov](https://codecov.io/github/indydevs/flutter/branch/main/graph/badge.svg?token=XANF37D9C1)](https://codecov.io/github/indydevs/flutter)
5
5
  [![Gem](https://img.shields.io/gem/v/flutter)](https://rubygems.org/gems/flutter)
6
+ [![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/flutter)
6
7
 
7
8
  ```
8
9
  __ __
@@ -61,11 +62,13 @@ all the following conditions are true:
61
62
  ```
62
63
 
63
64
  #### With guard
64
- Add the following to your `Guardfile`:
65
+ Using the same configuration as above (and assuming that the application
66
+ sources are in the `./lib` folder while the tests are in the `./test` folder)
67
+ add the following to your `Guardfile`:
65
68
 
66
69
  ```ruby
67
70
  guard :minitest, test_folders: ["test"] do
68
- watch(%r{^{test,lib}/(.*/)?([^/]+)\.rb$}) { "test" }
71
+ watch(%r{^(test|lib)/(.*/)?([^/]+)\.rb$}) { "test" }
69
72
  end
70
73
  ```
71
74
 
@@ -87,7 +90,7 @@ end
87
90
  Flutter.configure do |config|
88
91
  config.enabled = true
89
92
  # Paths to consider when tracking test -> source mappings. Default: Dir.pwd/*
90
- config.sources = ["./app/*", "./test/*"]
93
+ config.sources = ["./app/*", "./lib/*", "./spec/*"]
91
94
  # Paths to ignore for tracking test -> source. Default: ./vendor
92
95
  config.exclusions = ["./vendor/*"]
93
96
  # Storage type. Default: Flutter::Persistence::Marshal
@@ -99,15 +102,65 @@ end
99
102
  end
100
103
  ```
101
104
  #### With guard
102
- Using the same configuration as above add the following to your `Guardfile`:
105
+ Using the same configuration as above (and assuming that the application
106
+ sources are in the `./app` & `./lib` folders while the specs are in the `./spec` folder)
107
+ add the following to your `Guardfile`:
103
108
 
104
109
  ```ruby
105
110
  guard :rspec, cmd: "rspec" do
106
- watch(%r{^{spec,lib}/(.*/)?([^/]+)\.rb$}) { "spec" }
111
+ watch(%r{^(spec|app|lib)/(.*/)?([^/]+)\.rb$}) { "spec" }
107
112
  end
108
113
  ```
109
114
  ## Configuring flutter in continuous integration
110
- **TODO**
115
+
116
+ Flutter can be used in continuous integration environments to speed up the turn
117
+ around time from running tests by only running tests affected by the changes
118
+ in a pull request.
119
+
120
+ ### Github Actions
121
+ The following example workflow with github actions does the following:
122
+ - Always run all tests on the `main` branch
123
+ - Only run tests affected by the "current" commit for CI workflows triggered by a `push` event on other branches
124
+ - If the CI workflow is triggered due to a `pull_request` event, run all tests affected by all commits in the branch
125
+ (by comparing against the branch point of the pull request)
126
+
127
+ ```yaml
128
+ # Get the commit where this branch diverges from origin/main
129
+ - name: Retrieve branch point
130
+ if: github.event_name == 'pull_request'
131
+ run: |
132
+ 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)"
133
+ id: cache_keys
134
+ # Use the always-upload-cache action to:
135
+ # - Restore the flutter state from cache from either the branch point (if it was set in the previous step)
136
+ # or the last run in the current branch
137
+ # - After the run cache the flutter state using the current commit hash as the hash key
138
+ - name: Setup flutter state
139
+ id: flutter-state
140
+ uses: pat-s/always-upload-cache@v2.1.5
141
+ env:
142
+ cache-name: cache-flutter-state
143
+ with:
144
+ path: .flutter
145
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ github.sha }}
146
+ restore-keys: |
147
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.ruby-version }}-${{ steps.cache_keys.outputs.KEY }}
148
+ # If this is a push event on the main branch, clear the flutter state
149
+ # so that all tests are run and a full state is cached on the main branch
150
+ - name: Clear flutter state
151
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/main')
152
+ run: rm -rf .flutter
153
+ ```
154
+ > **Note**
155
+ > The exact CI configuration would ofcourse depend on your workflow and confidence in selectively
156
+ > running tests for pull requests.
157
+
158
+ > **Warning**
159
+ > Selectively running tests in a pull request would show a drop in coverage if you are collecting
160
+ > and/or using code coverage as a "Check". One way to make Flutter work hand in hand with code
161
+ > coverage checks is to only validate that the diff in the pull request has a 100% coverage. For
162
+ > example with [codecov](https://docs.codecov.com/docs/commit-status#section-project-status) this can be
163
+ > achieved by only enabling the `project` status for the main branch and `patch` status otherwise.
111
164
 
112
165
  ## Related work
113
166
 
@@ -119,9 +172,19 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
119
172
 
120
173
  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
174
 
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`
175
+ To install this gem onto your local machine, run `bundle exec rake install`.
176
+
177
+ ### Releasing a new version
178
+ - Ensure that the [Unreleased](./CHANGELOG.md#Unreleased) section of the changelog is up to date
179
+ and contains useful details.
180
+ - Create a new release using the `release` rake task as follows (for more details about specifying the version change
181
+ run `gem bump --help` which is the command used by the task):
182
+ - Patch release `bundle exec rake release["-v patch"]`
183
+ - Minor release `bundle exec rake release["-v minor"]`
184
+ - Major release `bundle exec rake release["-v major"]`
185
+ > **Note**
186
+ > The `release` rake task automates updating the changelog & version, committing the changes & creating a new tag
187
+ - Push the tag. The CI workflow for tag pushes will take care of publishing the gem & creating a github release.
125
188
 
126
189
  ## Contributing
127
190
 
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,35 @@ 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 = lines.slice_after { |line| line.include?("## #{version}") }.to_a[1] || []
55
+ puts chunk.join("\n")
56
+ end
data/TODO.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## Code quality
10
10
  - [ ] Separate public/private API
11
- - [ ] Add rdoc / yard documentation
11
+ - [x] Add rdoc / yard documentation
12
12
 
13
13
  ## Testing
14
14
  - [x] Improve test coverage
@@ -26,5 +26,5 @@
26
26
  ## Usability
27
27
  - [x] Quick start guide
28
28
  - [x] Recipes for guard
29
- - [ ] Recipes for CI
29
+ - [x] Recipes for CI
30
30
 
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.1"
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.1
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-02 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: []