From 0c0c0b80c696ff9e1d096592d7f3c4f986f030ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Fri, 29 Jan 2021 15:09:54 +0100 Subject: [PATCH] Add pub test and flutter test pre-push hooks --- README.md | 2 + config/default.yml | 12 ++ lib/overcommit/hook/pre_push/flutter_test.rb | 16 ++ lib/overcommit/hook/pre_push/pub_test.rb | 16 ++ .../hook/pre_push/flutter_test_spec.rb | 167 ++++++++++++++++++ .../overcommit/hook/pre_push/pub_test_spec.rb | 61 +++++++ 6 files changed, 274 insertions(+) create mode 100644 lib/overcommit/hook/pre_push/flutter_test.rb create mode 100644 lib/overcommit/hook/pre_push/pub_test.rb create mode 100644 spec/overcommit/hook/pre_push/flutter_test_spec.rb create mode 100644 spec/overcommit/hook/pre_push/pub_test_spec.rb diff --git a/README.md b/README.md index e1c3911b..c77d4126 100644 --- a/README.md +++ b/README.md @@ -580,10 +580,12 @@ but before any objects have been transferred. If a hook fails, the push is aborted. * [Brakeman](lib/overcommit/hook/pre_push/brakeman.rb) +* [FlutterTest](lib/overcommit/hook/pre_push/flutter_test.rb) * [Minitest](lib/overcommit/hook/pre_push/minitest.rb) * [PhpUnit](lib/overcommit/hook/pre_push/php_unit.rb) * [Pronto](lib/overcommit/hook/pre_push/pronto.rb) * [ProtectedBranches](lib/overcommit/hook/pre_push/protected_branches.rb) +* [PubTest](lib/overcommit/hook/pre_push/pub_test.rb) * [Pytest](lib/overcommit/hook/pre_push/pytest.rb) * [PythonNose](lib/overcommit/hook/pre_push/python_nose.rb) * [RakeTarget](lib/overcommit/hook/pre_push/rake_target.rb) diff --git a/config/default.yml b/config/default.yml index 748476e8..a55b699e 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1276,6 +1276,12 @@ PrePush: flags: ['test'] include: 'src/**/*.rs' + FlutterTest: + enabled: false + description: 'Run flutter test suite' + required_executable: 'flutter' + flags: ['test'] + GitLfs: enabled: false description: 'Upload files tracked by Git LFS' @@ -1322,6 +1328,12 @@ PrePush: destructive_only: true branches: ['master'] + PubTest: + enabled: false + description: 'Run pub test suite' + required_executable: 'pub' + flags: ['run', 'test'] + Pytest: enabled: false description: 'Run pytest test suite' diff --git a/lib/overcommit/hook/pre_push/flutter_test.rb b/lib/overcommit/hook/pre_push/flutter_test.rb new file mode 100644 index 00000000..26c987cf --- /dev/null +++ b/lib/overcommit/hook/pre_push/flutter_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs Flutter test suite (`flutter test`) before push + # + # @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html + class FlutterTest < Base + def run + result = execute(command) + return :pass if result.success? + + output = result.stdout + result.stderr + [:fail, output] + end + end +end diff --git a/lib/overcommit/hook/pre_push/pub_test.rb b/lib/overcommit/hook/pre_push/pub_test.rb new file mode 100644 index 00000000..0dbfb0bf --- /dev/null +++ b/lib/overcommit/hook/pre_push/pub_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Overcommit::Hook::PrePush + # Runs Dart test suite (`pub run test`) before push + # + # @see https://pub.dev/packages/test#running-tests + class PubTest < Base + def run + result = execute(command) + return :pass if result.success? + + output = result.stdout + result.stderr + [:fail, output] + end + end +end diff --git a/spec/overcommit/hook/pre_push/flutter_test_spec.rb b/spec/overcommit/hook/pre_push/flutter_test_spec.rb new file mode 100644 index 00000000..a5bce582 --- /dev/null +++ b/spec/overcommit/hook/pre_push/flutter_test_spec.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::FlutterTest do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when flutter test exits successfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when flutter test exits unsuccessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'with a runtime error' do + before do + result.stub(stdout: '', stderr: <<-MSG) + 0:03 +0: Counter increments smoke test + ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ + The following _Exception was thrown running a test: + Exception + + When the exception was thrown, this was the stack: + #0 main. (file:///Users/user/project/test/widget_test.dart:18:5) + + #1 main. (file:///Users/user/project/test/widget_test.dart) + #2 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29) + + #3 testWidgets.. (package:flutter_test/src/widget_tester.dart) + #4 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19) + + #7 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14) + #8 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24) + #9 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54) + #14 withClock (package:clock/src/default.dart:48:10) + #15 FakeAsync.run. (package:fake_async/fake_async.dart:178:22) + #20 FakeAsync.run (package:fake_async/fake_async.dart:178:7) + #21 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15) + #22 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24) + #23 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19) + + #24 Declarer.test.. (package:test_api/src/backend/declarer.dart) + #29 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13) + #30 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15) + #35 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5) + #36 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17) + + #37 Invoker._onRun... (package:test_api/src/backend/invoker.dart) + #42 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9) + #43 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15) + #44 Invoker._onRun. (package:test_api/src/backend/invoker.dart:369:7) + #51 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11) + #52 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11) + #53 RemoteListener._runLiveTest. (package:test_api/src/remote_listener.dart:256:16) + #58 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5) + #59 RemoteListener._serializeTest. (package:test_api/src/remote_listener.dart:208:7) + #77 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12) + #78 new _MultiChannel. (package:stream_channel/src/multi_channel.dart:159:31) + #82 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11) + #116 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1145:21) + #124 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23) + #125 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46) + #135 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41) + #144 new _RawSocket. (dart:io-patch/socket_patch.dart:1580:33) + #145 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14) + (elided 111 frames from dart:async and package:stack_trace) + + The test description was: + Counter increments smoke test + ════════════════════════════════════════════════════════════════════════════════════════════════════ + 00:03 +0 -1: Counter increments smoke test [E] + Test failed. See exception logs above. + The test description was: Counter increments smoke test + + 00:03 +0 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + + context 'with a test failure' do + before do + result.stub(stderr: '', stdout: <<-MSG) + 00:02 +0: Counter increments smoke test + ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ + The following TestFailure object was thrown running a test: + Expected: exactly one matching node in the widget tree + Actual: _TextFinder: + Which: means none were found but one was expected + + When the exception was thrown, this was the stack: + #4 main. (file:///Users/user/project/test/widget_test.dart:19:5) + + #5 main. (file:///Users/user/project/test/widget_test.dart) + #6 testWidgets.. (package:flutter_test/src/widget_tester.dart:146:29) + + #7 testWidgets.. (package:flutter_test/src/widget_tester.dart) + #8 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19) + + #11 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14) + #12 AutomatedTestWidgetsFlutterBinding.runTest. (package:flutter_test/src/binding.dart:1173:24) + #13 FakeAsync.run.. (package:fake_async/fake_async.dart:178:54) + #18 withClock (package:clock/src/default.dart:48:10) + #19 FakeAsync.run. (package:fake_async/fake_async.dart:178:22) + #24 FakeAsync.run (package:fake_async/fake_async.dart:178:7) + #25 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15) + #26 testWidgets. (package:flutter_test/src/widget_tester.dart:138:24) + #27 Declarer.test.. (package:test_api/src/backend/declarer.dart:175:19) + + #28 Declarer.test.. (package:test_api/src/backend/declarer.dart) + #33 Declarer.test. (package:test_api/src/backend/declarer.dart:173:13) + #34 Invoker.waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:231:15) + #39 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5) + #40 Invoker._onRun... (package:test_api/src/backend/invoker.dart:383:17) + + #41 Invoker._onRun... (package:test_api/src/backend/invoker.dart) + #46 Invoker._onRun.. (package:test_api/src/backend/invoker.dart:370:9) + #47 Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15) + #48 Invoker._onRun. (package:test_api/src/backend/invoker.dart:369:7) + #55 Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11) + #56 LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11) + #57 RemoteListener._runLiveTest. (package:test_api/src/remote_listener.dart:256:16) + #62 RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5) + #63 RemoteListener._serializeTest. (package:test_api/src/remote_listener.dart:208:7) + #81 _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12) + #82 new _MultiChannel. (package:stream_channel/src/multi_channel.dart:159:31) + #86 CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11) + #120 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1145:21) + #128 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23) + #129 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46) + #139 _Socket._onData (dart:io-patch/socket_patch.dart:2044:41) + #148 new _RawSocket. (dart:io-patch/socket_patch.dart:1580:33) + #149 _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14) + (elided 111 frames from dart:async and package:stack_trace) + + This was caught by the test expectation on the following line: + file:///Users/user/project/test/widget_test.dart line 19 + The test description was: + Counter increments smoke test + ════════════════════════════════════════════════════════════════════════════════════════════════════ + 00:02 +0 -1: Counter increments smoke test [E] + Test failed. See exception logs above. + The test description was: Counter increments smoke test + + 00:02 +0 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + end +end diff --git a/spec/overcommit/hook/pre_push/pub_test_spec.rb b/spec/overcommit/hook/pre_push/pub_test_spec.rb new file mode 100644 index 00000000..41ff5c5d --- /dev/null +++ b/spec/overcommit/hook/pre_push/pub_test_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Overcommit::Hook::PrePush::PubTest do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + context 'when pub test exits successfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(true) + subject.stub(:execute).and_return(result) + end + + it { should pass } + end + + context 'when pub test exits unsuccessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + subject.stub(:execute).and_return(result) + end + + context 'with a runtime error' do + before do + result.stub(stdout: '', stderr: <<-MSG) + 00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E] + Exception + test/test_test.dart 6:5 main. + + 00:01 +1 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + + context 'with a test failure' do + before do + result.stub(stderr: '', stdout: <<-MSG) + 00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E] + Expected: ['fooo', 'bar', 'baz'] + Actual: ['foo', 'bar', 'baz'] + Which: at location [0] is 'foo' instead of 'fooo' + + package:test_api expect + test/test_test.dart 6:5 main. + + 00:01 +1 -1: Some tests failed. + MSG + end + + it { should fail_hook } + end + end +end