flutter 0.1.0.pre.3 → 0.2.1

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: 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: []