danger-dangermattic 1.2.2 → 1.2.4

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.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile.lock +95 -57
  5. data/lib/dangermattic/gem_version.rb +1 -1
  6. data/lib/dangermattic/plugins/android_unit_test_checker.rb +7 -4
  7. data/lib/dangermattic/plugins/manifest_pr_checker.rb +25 -8
  8. data/lib/dangermattic/plugins/podfile_checker.rb +2 -0
  9. data/lib/dangermattic/plugins/pr_size_checker.rb +10 -12
  10. data/rakelib/console.rake +1 -1
  11. data/rakelib/git_helpers.rake +1 -1
  12. data/spec/android_unit_test_checker_spec.rb +93 -3
  13. data/spec/fixtures/android_unit_test_checker/MixDataPlusNormalClass.kt +14 -0
  14. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/api/ApiResult.kt +69 -0
  15. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/api/WebRecordApi.kt +49 -0
  16. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/domain/drive/UiStates.kt +13 -0
  17. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/main/settings/integrations/connect/AppIntegrationModule.kt +29 -0
  18. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/main/streaks/StreaksViewModel.kt +377 -0
  19. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/mediastorage/MediaStorageConfiguration.kt +31 -0
  20. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/utils/AccountType.kt +15 -0
  21. data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/utils/usecase/SelectPhotoUseCase.kt +76 -0
  22. data/spec/pr_size_checker_spec.rb +27 -26
  23. data/spec/spec_helper.rb +8 -2
  24. data/spec/view_changes_checker_spec.rb +15 -15
  25. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dee5f031f75e2069d6d197e11a2c20be5e8bdd24792e04c36733632886bcbe56
4
- data.tar.gz: 6375edfd326a90f60eb89b073d43896582fe23626624ccf08c148ada39bdacb2
3
+ metadata.gz: ad580440b78f9c9bd16790cb679c1a38a2ab32f22e9936f12e88398cef578a9f
4
+ data.tar.gz: ca179653e7759647821a2653f659ff04d56d38ce725993a31454f493180f1ffa
5
5
  SHA512:
6
- metadata.gz: 2c6fdb6234e0f0957b20ab49aa4fc855a546edd73ac26b6d0ecafd8808a2dc5168530e784dc0341044684e0477e77b8d528bf5922753f6286dca47ff8f8d5407
7
- data.tar.gz: 64ec129dac49b60046827c2ca42f91728fc395202cfbe2e4cf8d34773113fd6da4064c02f4f971fab9736f7a2842fd86f9f25d23c6208257c862d55fe7b9c39c
6
+ metadata.gz: a30553a0ff163ac72fa5492fc6db0047bf8e7569241a8cdce1de71bf8e8224703a271ca32afe0947ba61a751bcfbb048ed744c22e527513a2d0ae26adbd40ef0
7
+ data.tar.gz: f7657a8e47f25a63169a0e965a067792df749ee3fd8468199744dff509083f8b292da4475bef161bd38edcfe3acbf1c747e7a929d24136b3cd59bce0130e408c
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rake
3
3
  - rubocop-rspec
4
4
 
data/CHANGELOG.md CHANGED
@@ -20,6 +20,18 @@ _None_
20
20
 
21
21
  _None_
22
22
 
23
+ ## 1.2.4
24
+
25
+ ### Internal Changes
26
+
27
+ - `pr_size_checker` and `manifest_pr_checker`: optimize performance for large PRs [#103]
28
+
29
+ ## 1.2.3
30
+
31
+ ### Bug Fixes
32
+
33
+ - `android_unit_test_checker`: add `annotation` classes as an exception when reporting missing Android unit tests [#101]
34
+
23
35
  ## 1.2.2
24
36
 
25
37
  ### Bug Fixes
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- danger-dangermattic (1.2.2)
4
+ danger-dangermattic (1.2.4)
5
5
  danger (~> 9.4)
6
6
  danger-plugin-api (~> 1.0)
7
7
  danger-rubocop (~> 0.13)
@@ -10,10 +10,25 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
+ activesupport (8.0.2)
14
+ base64
15
+ benchmark (>= 0.3)
16
+ bigdecimal
17
+ concurrent-ruby (~> 1.0, >= 1.3.1)
18
+ connection_pool (>= 2.2.5)
19
+ drb
20
+ i18n (>= 1.6, < 2)
21
+ logger (>= 1.4.2)
22
+ minitest (>= 5.1)
23
+ securerandom (>= 0.3)
24
+ tzinfo (~> 2.0, >= 2.0.5)
25
+ uri (>= 0.13.1)
13
26
  addressable (2.8.7)
14
27
  public_suffix (>= 2.0.2, < 7.0)
15
- ast (2.4.2)
16
- base64 (0.2.0)
28
+ ast (2.4.3)
29
+ base64 (0.3.0)
30
+ benchmark (0.4.1)
31
+ bigdecimal (3.2.2)
17
32
  claide (1.1.0)
18
33
  claide-plugins (0.9.2)
19
34
  cork
@@ -21,48 +36,55 @@ GEM
21
36
  open4 (~> 1.3)
22
37
  coderay (1.1.3)
23
38
  colored2 (3.1.2)
39
+ concurrent-ruby (1.3.5)
40
+ connection_pool (2.5.3)
24
41
  cork (0.3.0)
25
42
  colored2 (~> 3.1)
26
- danger (9.5.1)
43
+ danger (9.5.3)
27
44
  base64 (~> 0.2)
28
45
  claide (~> 1.0)
29
46
  claide-plugins (>= 0.9.2)
30
- colored2 (~> 3.1)
47
+ colored2 (>= 3.1, < 5)
31
48
  cork (~> 0.1)
32
49
  faraday (>= 0.9.0, < 3.0)
33
50
  faraday-http-cache (~> 2.0)
34
- git (~> 1.13)
35
- kramdown (~> 2.3)
51
+ git (>= 1.13, < 3.0)
52
+ kramdown (>= 2.5.1, < 3.0)
36
53
  kramdown-parser-gfm (~> 1.0)
37
54
  octokit (>= 4.0)
38
55
  pstore (~> 0.1)
39
- terminal-table (>= 1, < 4)
56
+ terminal-table (>= 1, < 5)
40
57
  danger-plugin-api (1.0.0)
41
58
  danger (> 2.0)
42
59
  danger-rubocop (0.13.0)
43
60
  danger
44
61
  rubocop (~> 1.0)
45
- diff-lcs (1.5.1)
46
- faraday (2.12.0)
47
- faraday-net_http (>= 2.0, < 3.4)
62
+ diff-lcs (1.6.2)
63
+ drb (2.2.3)
64
+ faraday (2.13.4)
65
+ faraday-net_http (>= 2.0, < 3.5)
48
66
  json
49
67
  logger
50
68
  faraday-http-cache (2.5.1)
51
69
  faraday (>= 0.8)
52
- faraday-net_http (3.3.0)
53
- net-http
54
- ffi (1.17.0)
55
- ffi (1.17.0-arm64-darwin)
56
- formatador (1.1.0)
57
- git (1.19.1)
70
+ faraday-net_http (3.4.1)
71
+ net-http (>= 0.5.0)
72
+ ffi (1.17.2)
73
+ ffi (1.17.2-arm64-darwin)
74
+ formatador (1.1.1)
75
+ git (2.3.3)
76
+ activesupport (>= 5.0)
58
77
  addressable (~> 2.8)
78
+ process_executer (~> 1.1)
59
79
  rchardet (~> 1.8)
60
- guard (2.19.0)
80
+ guard (2.19.1)
61
81
  formatador (>= 0.2.4)
62
82
  listen (>= 2.7, < 4.0)
83
+ logger (~> 1.6)
63
84
  lumberjack (>= 1.0.12, < 2.0)
64
85
  nenv (~> 0.1)
65
86
  notiffany (~> 0.0)
87
+ ostruct (~> 0.6)
66
88
  pry (>= 0.13.0)
67
89
  shellany (~> 0.0)
68
90
  thor (>= 0.18.1)
@@ -71,86 +93,102 @@ GEM
71
93
  guard (~> 2.1)
72
94
  guard-compat (~> 1.1)
73
95
  rspec (>= 2.99.0, < 4.0)
74
- json (2.7.5)
75
- kramdown (2.4.0)
76
- rexml
96
+ i18n (1.14.7)
97
+ concurrent-ruby (~> 1.0)
98
+ json (2.13.2)
99
+ kramdown (2.5.1)
100
+ rexml (>= 3.3.9)
77
101
  kramdown-parser-gfm (1.1.0)
78
102
  kramdown (~> 2.0)
79
- language_server-protocol (3.17.0.3)
103
+ language_server-protocol (3.17.0.5)
104
+ lint_roller (1.1.0)
80
105
  listen (3.9.0)
81
106
  rb-fsevent (~> 0.10, >= 0.10.3)
82
107
  rb-inotify (~> 0.9, >= 0.9.10)
83
- logger (1.6.1)
84
- lumberjack (1.2.10)
108
+ logger (1.7.0)
109
+ lumberjack (1.4.0)
85
110
  method_source (1.1.0)
111
+ minitest (5.25.5)
86
112
  nap (1.1.0)
87
113
  nenv (0.3.0)
88
- net-http (0.4.1)
114
+ net-http (0.6.0)
89
115
  uri
90
116
  notiffany (0.1.3)
91
117
  nenv (~> 0.1)
92
118
  shellany (~> 0.0)
93
- octokit (9.2.0)
119
+ octokit (10.0.0)
94
120
  faraday (>= 1, < 3)
95
121
  sawyer (~> 0.9)
96
122
  open4 (1.3.4)
97
- parallel (1.26.3)
98
- parser (3.3.5.1)
123
+ ostruct (0.6.3)
124
+ parallel (1.27.0)
125
+ parser (3.3.9.0)
99
126
  ast (~> 2.4.1)
100
127
  racc
101
- pry (0.14.2)
128
+ prism (1.4.0)
129
+ process_executer (1.3.0)
130
+ pry (0.15.2)
102
131
  coderay (~> 1.1)
103
132
  method_source (~> 1.0)
104
- pstore (0.1.3)
105
- public_suffix (6.0.1)
133
+ pstore (0.2.0)
134
+ public_suffix (6.0.2)
106
135
  racc (1.8.1)
107
136
  rainbow (3.1.1)
108
- rake (13.2.1)
137
+ rake (13.3.0)
109
138
  rb-fsevent (0.11.2)
110
139
  rb-inotify (0.11.1)
111
140
  ffi (~> 1.0)
112
- rchardet (1.8.0)
113
- regexp_parser (2.9.2)
114
- rexml (3.3.9)
115
- rspec (3.13.0)
141
+ rchardet (1.9.0)
142
+ regexp_parser (2.11.0)
143
+ rexml (3.4.2)
144
+ rspec (3.13.1)
116
145
  rspec-core (~> 3.13.0)
117
146
  rspec-expectations (~> 3.13.0)
118
147
  rspec-mocks (~> 3.13.0)
119
- rspec-core (3.13.2)
148
+ rspec-core (3.13.5)
120
149
  rspec-support (~> 3.13.0)
121
- rspec-expectations (3.13.3)
150
+ rspec-expectations (3.13.5)
122
151
  diff-lcs (>= 1.2.0, < 2.0)
123
152
  rspec-support (~> 3.13.0)
124
- rspec-mocks (3.13.2)
153
+ rspec-mocks (3.13.5)
125
154
  diff-lcs (>= 1.2.0, < 2.0)
126
155
  rspec-support (~> 3.13.0)
127
- rspec-support (3.13.1)
128
- rubocop (1.68.0)
156
+ rspec-support (3.13.4)
157
+ rubocop (1.79.2)
129
158
  json (~> 2.3)
130
- language_server-protocol (>= 3.17.0)
159
+ language_server-protocol (~> 3.17.0.2)
160
+ lint_roller (~> 1.1.0)
131
161
  parallel (~> 1.10)
132
162
  parser (>= 3.3.0.2)
133
163
  rainbow (>= 2.2.2, < 4.0)
134
- regexp_parser (>= 2.4, < 3.0)
135
- rubocop-ast (>= 1.32.2, < 2.0)
164
+ regexp_parser (>= 2.9.3, < 3.0)
165
+ rubocop-ast (>= 1.46.0, < 2.0)
136
166
  ruby-progressbar (~> 1.7)
137
- unicode-display_width (>= 2.4.0, < 3.0)
138
- rubocop-ast (1.33.0)
139
- parser (>= 3.3.1.0)
140
- rubocop-rake (0.6.0)
141
- rubocop (~> 1.0)
142
- rubocop-rspec (3.2.0)
143
- rubocop (~> 1.61)
167
+ unicode-display_width (>= 2.4.0, < 4.0)
168
+ rubocop-ast (1.46.0)
169
+ parser (>= 3.3.7.2)
170
+ prism (~> 1.4)
171
+ rubocop-rake (0.7.1)
172
+ lint_roller (~> 1.1)
173
+ rubocop (>= 1.72.1)
174
+ rubocop-rspec (3.6.0)
175
+ lint_roller (~> 1.1)
176
+ rubocop (~> 1.72, >= 1.72.1)
144
177
  ruby-progressbar (1.13.0)
145
178
  sawyer (0.9.2)
146
179
  addressable (>= 2.3.5)
147
180
  faraday (>= 0.17.3, < 3)
181
+ securerandom (0.4.1)
148
182
  shellany (0.0.1)
149
- terminal-table (3.0.2)
150
- unicode-display_width (>= 1.1.1, < 3)
151
- thor (1.3.2)
152
- unicode-display_width (2.6.0)
153
- uri (0.13.1)
183
+ terminal-table (4.0.0)
184
+ unicode-display_width (>= 1.1.1, < 4)
185
+ thor (1.4.0)
186
+ tzinfo (2.0.6)
187
+ concurrent-ruby (~> 1.0)
188
+ unicode-display_width (3.1.4)
189
+ unicode-emoji (~> 4.0, >= 4.0.4)
190
+ unicode-emoji (4.0.4)
191
+ uri (1.0.3)
154
192
  yard (0.9.37)
155
193
 
156
194
  PLATFORMS
@@ -170,4 +208,4 @@ DEPENDENCIES
170
208
  yard
171
209
 
172
210
  BUNDLED WITH
173
- 2.4.13
211
+ 2.6.8
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dangermattic
4
- VERSION = '1.2.2'
4
+ VERSION = '1.2.4'
5
5
  end
@@ -24,14 +24,15 @@ module Danger
24
24
  #
25
25
  class AndroidUnitTestChecker < Plugin
26
26
  ANY_CLASS_DETECTOR = /class\s+([A-Z]\w+)\s*(.*?)\s*{/m
27
- CLASS_MODIFIER_DETECTOR = /((?:\s|public|internal|protected|private|final|abstract|static|data|enum|sealed|value)*)class\s+([A-Z]\w+)\s*(.*?)\s*{/m
27
+ CLASS_MODIFIER_DETECTOR = /((?:\s|public|internal|protected|private|final|abstract|static|data|enum|sealed|value|annotation)*)class\s+([A-Z]\w+)\s*(.*?)\s*({|\n\n)/m
28
28
 
29
29
  CLASS_MODIFIER_EXCEPTIONS = [
30
30
  /\s*data\s*/,
31
31
  /\s*private\s*/,
32
32
  /\s*enum\s*/,
33
33
  /\s*sealed\s*/,
34
- /\s*value\s*/
34
+ /\s*value\s*/,
35
+ /\s*annotation\s*/
35
36
  ].freeze
36
37
 
37
38
  DEFAULT_CLASSES_EXCEPTIONS = [
@@ -95,6 +96,7 @@ module Danger
95
96
  private
96
97
 
97
98
  ClassViolation = Struct.new(:classname, :file)
99
+ private_constant :ClassViolation
98
100
 
99
101
  # @param git_diff [Git::Diff] the git diff object
100
102
  # @param classes_exceptions [Array<String>] Regexes matching class names to exclude from the check.
@@ -151,7 +153,7 @@ module Danger
151
153
  # @return [Array<ClassViolation>] An array of ClassViolation objects representing the violations found.
152
154
  def find_violations(path:, diff_patch:, classes_exceptions:, subclasses_exceptions:)
153
155
  added_lines = git_utils.added_lines(diff_patch: diff_patch)
154
- matches = added_lines.scan(CLASS_MODIFIER_DETECTOR)
156
+ matches = "#{added_lines}\n".scan(CLASS_MODIFIER_DETECTOR) # add a newline to ensure the regex matches the last class in the file
155
157
  matches.reject! do |m|
156
158
  class_match_is_exception?(
157
159
  m,
@@ -180,10 +182,11 @@ module Danger
180
182
  # @param classes_exceptions [Array<String>] Regexes matching class names to exclude from the check.
181
183
  # @param subclasses_exceptions [Array<String>] Regexes matching base class names to exclude from the check
182
184
  #
183
- # @return [void]
185
+ # @return [Boolean]
184
186
  def class_match_is_exception?(match, file, classes_exceptions, subclasses_exceptions)
185
187
  return true if classes_exceptions.any? { |re| match[1] =~ re }
186
188
  return true if CLASS_MODIFIER_EXCEPTIONS.any? { |re| match[0] =~ re }
189
+ return true unless match[3].include?('{') # Ignore classes that don't have a body
187
190
 
188
191
  subclass_regexp = File.extname(file) == '.java' ? /extends\s+([A-Z]\w+)/m : /\s*:\s*([A-Z]\w+)/m
189
192
  subclass = match[2].scan(subclass_regexp)&.last&.last
@@ -29,7 +29,17 @@ module Danger
29
29
  #
30
30
  class ManifestPRChecker < Plugin
31
31
  MESSAGE = '`%s` was changed without updating its corresponding `%s`. %s.'
32
- SWIFT_INSTRUCTION = 'Please resolve the Swift packages as appropriate to your project setup (e.g. in Xcode or by running `swift package resolve`)'
32
+
33
+ # The two new lines at the start are intentional.
34
+ # This will be interpolated in MESSAGE into the final %s.
35
+ # The first new line moves it to a new line, the second adds visual padding.
36
+ SWIFT_INSTRUCTION = <<~INSTRUCTION
37
+
38
+
39
+ If the change includes adding, removing, or editing a dependency please resolve the Swift packages as appropriate to your project setup (e.g. in Xcode or by running `swift package resolve`).
40
+
41
+ If the change to the `Package.swift` did not modify dependencies, ignoring this warning should be safe, but we recommend double checking and running the package resolution just in case.
42
+ INSTRUCTION
33
43
 
34
44
  # Performs all the checks, asserting that changes on `Gemfile`, `Podfile` and `Package.swift` must have corresponding
35
45
  # lock file changes.
@@ -88,6 +98,8 @@ module Danger
88
98
  # Check if the `Package.swift` file was modified without a corresponding `Package.resolved` update,
89
99
  # checking for exact path matches
90
100
  #
101
+ # @param manifest_path [String] The path to the `Package.swift` file.
102
+ # @param manifest_lock_path [String] The path to the `Package.resolved` file.
91
103
  # @param report_type [Symbol] (optional) The type of report for the message. Types: :error, :warning (default), :message.
92
104
  #
93
105
  # @return [void]
@@ -103,13 +115,19 @@ module Danger
103
115
  private
104
116
 
105
117
  def check_manifest_lock_updated(file_name:, lock_file_name:, instruction:, report_type: :warning)
118
+ all_files = git_utils.all_changed_files
119
+
106
120
  # Find all the modified manifest files
107
- manifest_modified_files = git_utils.all_changed_files.select { |f| File.basename(f) == file_name }
121
+ manifest_modified_files = all_files.select { |f| File.basename(f) == file_name }
122
+
123
+ # Build a hash mapping directory -> set of basenames for O(1) lookup
124
+ files_by_dir = all_files.group_by { |f| File.dirname(f) }
125
+ .transform_values { |files| files.to_set { |f| File.basename(f) } }
108
126
 
109
127
  # For each manifest file, check if the corresponding lockfile (in the same dir) was also modified
110
128
  manifest_modified_files.each do |manifest_file|
111
- lockfile_modified = git_utils.all_changed_files.any? { |f| File.dirname(f) == File.dirname(manifest_file) && File.basename(f) == lock_file_name }
112
- next if lockfile_modified
129
+ manifest_dir = File.dirname(manifest_file)
130
+ next if files_by_dir[manifest_dir]&.include?(lock_file_name)
113
131
 
114
132
  message = format(MESSAGE, manifest_file, lock_file_name, instruction)
115
133
  reporter.report(message: message, type: report_type)
@@ -117,11 +135,10 @@ module Danger
117
135
  end
118
136
 
119
137
  def check_manifest_lock_updated_strict(manifest_path:, manifest_lock_path:, instruction:, report_type: :warning)
120
- manifest_modified = git_utils.all_changed_files.include?(manifest_path)
121
- return unless manifest_modified
138
+ all_files_set = git_utils.all_changed_files.to_set
122
139
 
123
- lockfile_modified = git_utils.all_changed_files.include?(manifest_lock_path)
124
- return if lockfile_modified
140
+ return unless all_files_set.include?(manifest_path)
141
+ return if all_files_set.include?(manifest_lock_path)
125
142
 
126
143
  message = format(MESSAGE, manifest_path, File.basename(manifest_lock_path), instruction)
127
144
  reporter.report(message: message, type: report_type)
@@ -86,7 +86,9 @@ module Danger
86
86
  private
87
87
 
88
88
  COMMIT_REFERENCE_REGEXP = /\(from `\S+`, commit `\S+`\)/
89
+ private_constant :COMMIT_REFERENCE_REGEXP
89
90
  BRANCH_REFERENCE_REGEXP = /\(from `\S+`, branch `\S+`\)/
91
+ private_constant :BRANCH_REFERENCE_REGEXP
90
92
 
91
93
  def check_podfile_does_not_match(
92
94
  regexp:,
@@ -76,13 +76,12 @@ module Danger
76
76
  def insertions_size(file_selector: nil)
77
77
  return danger.git.insertions unless file_selector
78
78
 
79
- filtered_files = git_utils.all_changed_files.select(&file_selector)
79
+ # Only check added and modified files - deleted files have 0 insertions
80
+ filtered_files = git_utils.added_and_modified_files.select(&file_selector)
80
81
 
81
82
  filtered_files.sum do |file|
82
- # stats for a file in the GitHub API might be nil, making `info_for_file()` crash
83
- next 0 if danger.git.diff.stats[:files][file].nil?
84
-
85
- danger.git.info_for_file(file)&.[](:insertions).to_i
83
+ # Use cached stats directly instead of calling info_for_file for each file
84
+ danger.git.diff.stats[:files][file]&.[](:insertions).to_i
86
85
  end
87
86
  end
88
87
 
@@ -97,10 +96,8 @@ module Danger
97
96
  filtered_files = git_utils.all_changed_files.select(&file_selector)
98
97
 
99
98
  filtered_files.sum do |file|
100
- # stats for a file in the GitHub API might be nil, making `info_for_file()` crash
101
- next 0 if danger.git.diff.stats[:files][file].nil?
102
-
103
- danger.git.info_for_file(file)&.[](:deletions).to_i
99
+ # Use cached stats directly instead of calling info_for_file for each file
100
+ danger.git.diff.stats[:files][file]&.[](:deletions).to_i
104
101
  end
105
102
  end
106
103
 
@@ -115,10 +112,11 @@ module Danger
115
112
  filtered_files = git_utils.all_changed_files.select(&file_selector)
116
113
 
117
114
  filtered_files.sum do |file|
118
- # stats for a file in the GitHub API might be nil, making `info_for_file()` crash
119
- next 0 if danger.git.diff.stats[:files][file].nil?
115
+ # Use cached stats directly instead of calling info_for_file for each file
116
+ stats = danger.git.diff.stats[:files][file]
117
+ next 0 unless stats
120
118
 
121
- danger.git.info_for_file(file)&.[](:deletions).to_i + danger.git.info_for_file(file)&.[](:insertions).to_i
119
+ stats[:deletions].to_i + stats[:insertions].to_i
122
120
  end
123
121
  end
124
122
  end
data/rakelib/console.rake CHANGED
@@ -38,7 +38,7 @@ module Console
38
38
  answer
39
39
  end
40
40
 
41
- def self.confirm(text)
41
+ def self.confirm?(text)
42
42
  color_puts("#{text} [y/n]?", color_code: GREEN)
43
43
  answer = $stdin.gets.chomp
44
44
  answer.downcase == 'y'
@@ -17,7 +17,7 @@ module GitHelper
17
17
  elsif branch_exists
18
18
  Rake.sh('git', 'checkout', release_branch)
19
19
  else # create it
20
- abort('Aborted, as not run from trunk nor release branch') unless current_branch == 'trunk' || Console.confirm("You are not on 'trunk', nor already on '#{release_branch}'. Do you really want to cut the release branch from #{current_branch}?")
20
+ abort('Aborted, as not run from trunk nor release branch') unless current_branch == 'trunk' || Console.confirm?("You are not on 'trunk', nor already on '#{release_branch}'. Do you really want to cut the release branch from #{current_branch}?")
21
21
 
22
22
  Rake.sh('git', 'checkout', '-b', release_branch)
23
23
  end
@@ -144,6 +144,93 @@ module Danger
144
144
  expect(@dangerfile).to not_report
145
145
  end
146
146
 
147
+ it 'ensures data classes with no {…} body don\'t mess up detection of subsequent classes in the same file' do
148
+ # Ensure the CLASS_MODIFIER_DETECTOR regex doesn't assume class declaration always ends with `{` marking the class body
149
+ # (which would lead the regex to either miss the data class or extend the regex to the next `{`… that potentially belongs to the next class)
150
+ # This is especially important given data classes might not have a body at all
151
+
152
+ mix_data_plus_normal_class_file = 'MixDataPlusNormalClass.kt'
153
+ data_and_normal_class_diff = generate_add_diff_from_fixtures([mix_data_plus_normal_class_file])
154
+
155
+ allow(@dangerfile.git).to receive(:diff).and_return(data_and_normal_class_diff)
156
+
157
+ @plugin.check_missing_tests
158
+
159
+ expect_class_names_match_report(class_names: ['DummyClassMissingTest'], error_report: @dangerfile.status_report[:errors])
160
+ end
161
+
162
+ context 'when detecting classes and class modifiers' do
163
+ let(:added_files) do
164
+ dayone_source_fixtures_subdir = %w[src main java com dayoneapp dayone]
165
+ Dir.glob('**/*.kt', base: fixture_path('android_unit_test_checker', dayone_source_fixtures_subdir))
166
+ .map { |file| File.join(dayone_source_fixtures_subdir, file) }
167
+ end
168
+ let(:diff) { generate_add_diff_from_fixtures(added_files) }
169
+
170
+ # ClassName => Expected to be detected (true) or ignored (false)
171
+ let(:expected_detected_classes) do
172
+ {
173
+ # ApiResult.kt
174
+ 'ApiResult' => false, # `sealed class ApiResult<T>`
175
+ 'Success' => false, # `data class Success<T>`
176
+ 'Empty' => false, # `class Empty<T> : ApiResult<T>()` (but no body)
177
+ 'Failure' => false, # `data class Failure<T>(…)`
178
+ 'FailureType' => false, # `enum class FailureType`
179
+
180
+ # WebRecordApi.kt
181
+ 'CursorTime' => false, # `value class CursorTime`
182
+ 'WebRecordChanges' => false, # `data class WebRecordChanges`
183
+
184
+ # UiStates.kt
185
+ 'LoadKeyUiState' => false, # `sealed class LoadKeyUiState`
186
+
187
+ # AppIntegrationModule.kt
188
+ 'AppIntegrationHandlers' => false, # `annotation class AppIntegrationHandlers`
189
+
190
+ # StreaksViewModel.kt
191
+ 'StreaksViewModel' => true, # `class StreaksViewModel`
192
+ 'Streaks' => false, # `data class Streaks`
193
+ 'JournalOptionsState' => false, # `class JournalOptionsState` (but no body)
194
+ 'StreakWeekDay' => false, # `data class StreakWeekDay`
195
+ 'DayJournaled' => false, # `value class DayJournaled`
196
+ 'StreakJournal' => false, # `data class StreakJournal`
197
+ 'DaysOfWeekList' => false, # `private class DaysOfWeekList`
198
+ 'PreviousDays' => false, # `class PreviousDays` (but no body)
199
+
200
+ # MediaStorageConfiguration.kt
201
+ 'CompressQuality' => false, # `value class CompressQuality`
202
+ 'MediaStorageConfiguration' => false, # `class MediaStorageConfiguration` (but no body)
203
+ 'ThumbnailsConfiguration' => false, # `class ThumbnailsConfiguration` (but no body)
204
+
205
+ # AccountType.kt
206
+ 'AccountType' => false, # `enum class AccountType`
207
+
208
+ # SelectPhotoUseCase.kt
209
+ 'SelectPhotoUseCase' => true, # `class SelectPhotoUseCase`
210
+ 'GetMultipleImages' => false, # `private class GetMultipleImages`
211
+ 'GetSingleImage' => false # `private class GetSingleImage`
212
+ }
213
+ end
214
+
215
+ before do
216
+ allow(@dangerfile.git).to receive(:diff).and_return(diff)
217
+ end
218
+
219
+ it 'reports only classes that don\'t have a modifier that is part of modifier exceptions' do
220
+ @plugin.check_missing_tests
221
+ expected_violating_classes = expected_detected_classes.filter_map { |k, v| k if v } # only keys whose value is true
222
+ expect_class_names_match_report(class_names: expected_violating_classes, error_report: @dangerfile.status_report[:errors])
223
+ end
224
+
225
+ it 'validates that the RegEx matches all classes from source code' do
226
+ # Mock detection of exceptions in order to make `check_missing_tests` report all classes regardless of modifiers
227
+ # This allows us to validate that our RegEx matches all classes from the source code in the first place
228
+ allow(@plugin).to receive(:class_match_is_exception?).and_return(false)
229
+ @plugin.check_missing_tests
230
+ expect_class_names_match_report(class_names: expected_detected_classes.keys, error_report: @dangerfile.status_report[:errors])
231
+ end
232
+ end
233
+
147
234
  it 'does not report that a PR with the tests bypass label is missing tests' do
148
235
  added_files = %w[
149
236
  Abc.java
@@ -301,7 +388,7 @@ module Danger
301
388
 
302
389
  def generate_add_diff_from_fixtures(paths)
303
390
  paths.map do |path|
304
- content = fixture(File.join('android_unit_test_checker', path))
391
+ content = fixture('android_unit_test_checker', path)
305
392
  diff_str = generate_add_diff(file_path: path, content: content)
306
393
 
307
394
  GitDiffStruct.new('new', path, diff_str)
@@ -310,7 +397,7 @@ module Danger
310
397
 
311
398
  def generate_delete_diff_from_fixtures(paths)
312
399
  paths.map do |path|
313
- content = fixture(File.join('android_unit_test_checker', path))
400
+ content = fixture('android_unit_test_checker', path)
314
401
  diff_str = generate_delete_diff(file_path: path, content: content)
315
402
 
316
403
  GitDiffStruct.new('deleted', path, diff_str)
@@ -348,7 +435,10 @@ module Danger
348
435
  end
349
436
 
350
437
  def expect_class_names_match_report(class_names:, error_report:)
351
- expect(class_names.length).to eq(error_report.length)
438
+ if error_report.length != class_names.length
439
+ reported_class_names = error_report.map { |e| e.match(/class `(.*?)`/)[1] }
440
+ expect(reported_class_names).to eq(class_names)
441
+ end
352
442
  class_names.zip(error_report).each do |cls, error|
353
443
  expect(error).to include "Please add tests for class `#{cls}`"
354
444
  end
@@ -0,0 +1,14 @@
1
+ import javax.inject.Inject
2
+ import javax.inject.Singleton
3
+
4
+ internal data class DummyDataClassNotNeedingTests(
5
+ val title: String,
6
+ val id: String
7
+ )
8
+
9
+ @Singleton
10
+ internal class DummyClassMissingTest @Inject constructor(
11
+ private val name: String,
12
+ ) : ParentClass {
13
+ private val loggingTag = 'mylogging'
14
+ }