Skip to content

Commit bba5b75

Browse files
authored
Add pub test and flutter test pre-push hooks (sds#740)
1 parent fd3c3c0 commit bba5b75

File tree

6 files changed

+274
-0
lines changed

6 files changed

+274
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,12 @@ but before any objects have been transferred. If a hook fails, the push is
581581
aborted.
582582

583583
* [Brakeman](lib/overcommit/hook/pre_push/brakeman.rb)
584+
* [FlutterTest](lib/overcommit/hook/pre_push/flutter_test.rb)
584585
* [Minitest](lib/overcommit/hook/pre_push/minitest.rb)
585586
* [PhpUnit](lib/overcommit/hook/pre_push/php_unit.rb)
586587
* [Pronto](lib/overcommit/hook/pre_push/pronto.rb)
587588
* [ProtectedBranches](lib/overcommit/hook/pre_push/protected_branches.rb)
589+
* [PubTest](lib/overcommit/hook/pre_push/pub_test.rb)
588590
* [Pytest](lib/overcommit/hook/pre_push/pytest.rb)
589591
* [PythonNose](lib/overcommit/hook/pre_push/python_nose.rb)
590592
* [RakeTarget](lib/overcommit/hook/pre_push/rake_target.rb)

config/default.yml

+12
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,12 @@ PrePush:
12841284
flags: ['test']
12851285
include: 'src/**/*.rs'
12861286

1287+
FlutterTest:
1288+
enabled: false
1289+
description: 'Run flutter test suite'
1290+
required_executable: 'flutter'
1291+
flags: ['test']
1292+
12871293
GitLfs:
12881294
enabled: false
12891295
description: 'Upload files tracked by Git LFS'
@@ -1330,6 +1336,12 @@ PrePush:
13301336
destructive_only: true
13311337
branches: ['master']
13321338

1339+
PubTest:
1340+
enabled: false
1341+
description: 'Run pub test suite'
1342+
required_executable: 'pub'
1343+
flags: ['run', 'test']
1344+
13331345
Pytest:
13341346
enabled: false
13351347
description: 'Run pytest test suite'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
module Overcommit::Hook::PrePush
4+
# Runs Flutter test suite (`flutter test`) before push
5+
#
6+
# @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html
7+
class FlutterTest < Base
8+
def run
9+
result = execute(command)
10+
return :pass if result.success?
11+
12+
output = result.stdout + result.stderr
13+
[:fail, output]
14+
end
15+
end
16+
end
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
module Overcommit::Hook::PrePush
4+
# Runs Dart test suite (`pub run test`) before push
5+
#
6+
# @see https://pub.dev/packages/test#running-tests
7+
class PubTest < Base
8+
def run
9+
result = execute(command)
10+
return :pass if result.success?
11+
12+
output = result.stdout + result.stderr
13+
[:fail, output]
14+
end
15+
end
16+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Overcommit::Hook::PrePush::FlutterTest do
6+
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
7+
let(:context) { double('context') }
8+
subject { described_class.new(config, context) }
9+
10+
context 'when flutter test exits successfully' do
11+
let(:result) { double('result') }
12+
13+
before do
14+
result.stub(:success?).and_return(true)
15+
subject.stub(:execute).and_return(result)
16+
end
17+
18+
it { should pass }
19+
end
20+
21+
context 'when flutter test exits unsuccessfully' do
22+
let(:result) { double('result') }
23+
24+
before do
25+
result.stub(:success?).and_return(false)
26+
subject.stub(:execute).and_return(result)
27+
end
28+
29+
context 'with a runtime error' do
30+
before do
31+
result.stub(stdout: '', stderr: <<-MSG)
32+
0:03 +0: Counter increments smoke test
33+
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
34+
The following _Exception was thrown running a test:
35+
Exception
36+
37+
When the exception was thrown, this was the stack:
38+
#0 main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart:18:5)
39+
<asynchronous suspension>
40+
#1 main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart)
41+
#2 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)
42+
<asynchronous suspension>
43+
#3 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
44+
#4 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
45+
<asynchronous suspension>
46+
#7 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
47+
#8 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)
48+
#9 FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)
49+
#14 withClock (package:clock/src/default.dart:48:10)
50+
#15 FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)
51+
#20 FakeAsync.run (package:fake_async/fake_async.dart:178:7)
52+
#21 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
53+
#22 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)
54+
#23 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)
55+
<asynchronous suspension>
56+
#24 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
57+
#29 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)
58+
#30 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)
59+
#35 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
60+
#36 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)
61+
<asynchronous suspension>
62+
#37 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)
63+
#42 Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)
64+
#43 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
65+
#44 Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)
66+
#51 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)
67+
#52 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)
68+
#53 RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)
69+
#58 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)
70+
#59 RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)
71+
#77 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)
72+
#78 new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)
73+
#82 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
74+
#116 new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)
75+
#124 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)
76+
#125 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)
77+
#135 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)
78+
#144 new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)
79+
#145 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)
80+
(elided 111 frames from dart:async and package:stack_trace)
81+
82+
The test description was:
83+
Counter increments smoke test
84+
════════════════════════════════════════════════════════════════════════════════════════════════════
85+
00:03 +0 -1: Counter increments smoke test [E]
86+
Test failed. See exception logs above.
87+
The test description was: Counter increments smoke test
88+
89+
00:03 +0 -1: Some tests failed.
90+
MSG
91+
end
92+
93+
it { should fail_hook }
94+
end
95+
96+
context 'with a test failure' do
97+
before do
98+
result.stub(stderr: '', stdout: <<-MSG)
99+
00:02 +0: Counter increments smoke test
100+
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
101+
The following TestFailure object was thrown running a test:
102+
Expected: exactly one matching node in the widget tree
103+
Actual: _TextFinder:<zero widgets with text "1" (ignoring offstage widgets)>
104+
Which: means none were found but one was expected
105+
106+
When the exception was thrown, this was the stack:
107+
#4 main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart:19:5)
108+
<asynchronous suspension>
109+
#5 main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart)
110+
#6 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)
111+
<asynchronous suspension>
112+
#7 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
113+
#8 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
114+
<asynchronous suspension>
115+
#11 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
116+
#12 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)
117+
#13 FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)
118+
#18 withClock (package:clock/src/default.dart:48:10)
119+
#19 FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)
120+
#24 FakeAsync.run (package:fake_async/fake_async.dart:178:7)
121+
#25 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
122+
#26 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)
123+
#27 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)
124+
<asynchronous suspension>
125+
#28 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
126+
#33 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)
127+
#34 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)
128+
#39 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
129+
#40 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)
130+
<asynchronous suspension>
131+
#41 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)
132+
#46 Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)
133+
#47 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
134+
#48 Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)
135+
#55 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)
136+
#56 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)
137+
#57 RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)
138+
#62 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)
139+
#63 RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)
140+
#81 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)
141+
#82 new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)
142+
#86 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
143+
#120 new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)
144+
#128 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)
145+
#129 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)
146+
#139 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)
147+
#148 new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)
148+
#149 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)
149+
(elided 111 frames from dart:async and package:stack_trace)
150+
151+
This was caught by the test expectation on the following line:
152+
file:///Users/user/project/test/widget_test.dart line 19
153+
The test description was:
154+
Counter increments smoke test
155+
════════════════════════════════════════════════════════════════════════════════════════════════════
156+
00:02 +0 -1: Counter increments smoke test [E]
157+
Test failed. See exception logs above.
158+
The test description was: Counter increments smoke test
159+
160+
00:02 +0 -1: Some tests failed.
161+
MSG
162+
end
163+
164+
it { should fail_hook }
165+
end
166+
end
167+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Overcommit::Hook::PrePush::PubTest do
6+
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
7+
let(:context) { double('context') }
8+
subject { described_class.new(config, context) }
9+
10+
context 'when pub test exits successfully' do
11+
let(:result) { double('result') }
12+
13+
before do
14+
result.stub(:success?).and_return(true)
15+
subject.stub(:execute).and_return(result)
16+
end
17+
18+
it { should pass }
19+
end
20+
21+
context 'when pub test exits unsuccessfully' do
22+
let(:result) { double('result') }
23+
24+
before do
25+
result.stub(:success?).and_return(false)
26+
subject.stub(:execute).and_return(result)
27+
end
28+
29+
context 'with a runtime error' do
30+
before do
31+
result.stub(stdout: '', stderr: <<-MSG)
32+
00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E]
33+
Exception
34+
test/test_test.dart 6:5 main.<fn>
35+
36+
00:01 +1 -1: Some tests failed.
37+
MSG
38+
end
39+
40+
it { should fail_hook }
41+
end
42+
43+
context 'with a test failure' do
44+
before do
45+
result.stub(stderr: '', stdout: <<-MSG)
46+
00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E]
47+
Expected: ['fooo', 'bar', 'baz']
48+
Actual: ['foo', 'bar', 'baz']
49+
Which: at location [0] is 'foo' instead of 'fooo'
50+
51+
package:test_api expect
52+
test/test_test.dart 6:5 main.<fn>
53+
54+
00:01 +1 -1: Some tests failed.
55+
MSG
56+
end
57+
58+
it { should fail_hook }
59+
end
60+
end
61+
end

0 commit comments

Comments
 (0)