| # coding: utf-8 |
| from __future__ import unicode_literals, division, absolute_import, print_function |
| |
| import sys |
| import threading |
| import unittest |
| from os import path |
| import time |
| import re |
| import shutil |
| import os |
| |
| import sublime |
| |
| import shellenv |
| import package_events |
| |
| if sys.version_info < (3,): |
| from Queue import Queue |
| else: |
| from queue import Queue |
| |
| from .mocks import GolangBuildMock |
| |
| |
| TEST_GOPATH = path.join(path.dirname(__file__), 'go_projects') |
| VIEW_SETTINGS = { |
| 'GOPATH': TEST_GOPATH, |
| 'GOOS': None, |
| 'GOARCH': None, |
| 'GOARM': None, |
| 'GO386': None, |
| 'GORACE': None |
| } |
| |
| CROSS_COMPILE_OS = 'darwin' if sys.platform != 'darwin' else 'linux' |
| |
| |
| class GolangBuildTests(unittest.TestCase): |
| |
| def setUp(self): |
| for subdir in ('pkg', 'bin', 'src'): |
| full_path = path.join(TEST_GOPATH, subdir) |
| for entry in os.listdir(full_path): |
| if entry in set(['.git-keep', 'good', 'bad']): |
| continue |
| entry_path = path.join(full_path, entry) |
| if path.isdir(entry_path): |
| shutil.rmtree(entry_path) |
| else: |
| os.remove(entry_path) |
| |
| def test_build(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build') |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go build" succeed?')) |
| |
| def test_build_flags(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build', {'flags': ['-v', '-x']}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go build" succeed and print all commands?')) |
| |
| def test_build_flags_from_settings(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| with GolangBuildMock(sublime_settings={'build:flags': ['-v', '-x']}): |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build') |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go build" succeed and print all commands?')) |
| |
| def test_install_flags_from_view_settings(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build', {'task': 'install'}) |
| |
| custom_view_settings = VIEW_SETTINGS.copy() |
| custom_view_settings['install:flags'] = ['-v', '-x'] |
| |
| result_queue = open_file(file_path, custom_view_settings, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go install" succeed and print all commands?')) |
| |
| def test_clean(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build', {'task': 'clean'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go clean" succeed?')) |
| |
| def test_test(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build', {'task': 'test'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go test" succeed?')) |
| |
| def test_install(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build', {'task': 'install'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go install" succeed?')) |
| |
| def test_cross_compile(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| begin_event = threading.Event() |
| |
| def _run_build(view, result_queue): |
| notify_user('Select %s/amd64 from quick panel' % CROSS_COMPILE_OS) |
| begin_event.set() |
| view.window().run_command('golang_build', {'task': 'cross_compile'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| begin_event.wait() |
| result = wait_build(result_queue, timeout=15) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did the cross-compile succeed?')) |
| |
| def test_get(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| begin_event = threading.Event() |
| |
| def _run_build(view, result_queue): |
| sublime.set_clipboard('github.com/golang/example/hello') |
| notify_user('Paste from the clipboard into the input panel') |
| begin_event.set() |
| view.window().run_command('golang_build_get') |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| begin_event.wait() |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go get" succeed?')) |
| |
| def test_get_flags(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| begin_event = threading.Event() |
| |
| def _run_build(view, result_queue): |
| sublime.set_clipboard('github.com/golang/example/hello') |
| notify_user('Paste from the clipboard into the input panel') |
| begin_event.set() |
| view.window().run_command('golang_build_get', {'flags': ['-v', '-d']}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| begin_event.wait() |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go get" download but not install?')) |
| |
| def test_get_flags_from_settings(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| with GolangBuildMock(sublime_settings={'get:flags': ['-v', '-d']}): |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build_get', {'url': 'github.com/golang/example/hello'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go get" download but not install?')) |
| |
| def test_get_url(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build_get', {'url': 'github.com/golang/example/hello'}) |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| self.assertTrue(confirm_user('Did "go get" succeed for "github.com/golang/example/hello"?')) |
| |
| def test_terminal(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build_terminal') |
| |
| open_file(file_path, VIEW_SETTINGS, _run_build) |
| self.assertTrue(confirm_user('Did a terminal open to Packages/Golang Build/dev/go_projects/src/good/?')) |
| |
| def test_build_bad(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'bad', 'hello.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build') |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('error', result) |
| self.assertTrue(confirm_user('Did "go build" fail?')) |
| |
| def test_build_cancel(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build') |
| |
| def _cancel_build(): |
| view.window().run_command('golang_build_cancel') |
| |
| sublime.set_timeout(_cancel_build, 50) |
| |
| # We perform a cross-compile so the user has time to interrupt the build |
| custom_view_settings = VIEW_SETTINGS.copy() |
| custom_view_settings['GOOS'] = CROSS_COMPILE_OS |
| custom_view_settings['GOARCH'] = 'amd64' |
| |
| result_queue = open_file(file_path, custom_view_settings, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('cancelled', result) |
| self.assertTrue(confirm_user('Was "go build" successfully cancelled?')) |
| |
| def test_build_reopen(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| view.window().run_command('golang_build') |
| |
| result_queue = open_file(file_path, VIEW_SETTINGS, _run_build) |
| result = wait_build(result_queue) |
| self.assertEqual('success', result) |
| |
| time.sleep(0.4) |
| |
| def _hide_panel(): |
| sublime.active_window().run_command('hide_panel') |
| sublime.set_timeout(_hide_panel, 1) |
| |
| time.sleep(0.4) |
| self.assertTrue(confirm_user('Was the build output hidden?')) |
| |
| def _reopen_panel(): |
| sublime.active_window().run_command('golang_build_reopen') |
| sublime.set_timeout(_reopen_panel, 1) |
| |
| time.sleep(0.4) |
| self.assertTrue(confirm_user('Was the build output reopened?')) |
| |
| def test_build_interrupt(self): |
| ensure_not_ui_thread() |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| begin_event = threading.Event() |
| second_begin_event = threading.Event() |
| |
| def _run_build(view, result_queue): |
| notify_user('Press the "Stop Running Build" button when prompted') |
| |
| begin_event.set() |
| view.window().run_command('golang_build') |
| |
| def _new_build(): |
| view.window().run_command('golang_build') |
| second_begin_event.set() |
| |
| sublime.set_timeout(_new_build, 50) |
| |
| # We perform a cross-compile so the user has time to interrupt the build |
| custom_view_settings = VIEW_SETTINGS.copy() |
| custom_view_settings['GOOS'] = CROSS_COMPILE_OS |
| custom_view_settings['GOARCH'] = 'amd64' |
| |
| result_queue = open_file(file_path, custom_view_settings, _run_build) |
| begin_event.wait() |
| result1 = wait_build(result_queue) |
| self.assertEqual('cancelled', result1) |
| second_begin_event.wait() |
| result2 = wait_build(result_queue) |
| self.assertEqual('success', result2) |
| self.assertTrue(confirm_user('Was the first build cancelled and the second successful?')) |
| |
| def test_build_go_missing(self): |
| ensure_not_ui_thread() |
| |
| shell, _ = shellenv.get_env() |
| search_path = path.expanduser('~') |
| with GolangBuildMock(shell=shell, env={'PATH': search_path}): |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| notify_user('Press the "Open Documentation" button when prompted about go not being found in the PATH') |
| |
| view.window().run_command('golang_build') |
| |
| open_file(file_path, VIEW_SETTINGS, _run_build) |
| time.sleep(0.5) |
| self.assertTrue(confirm_user('Were you prompted that go could not be found in the PATH?')) |
| self.assertTrue(confirm_user('When you pressed "Open Documentation", was it opened in your browser?')) |
| |
| def test_build_no_gopath(self): |
| ensure_not_ui_thread() |
| |
| shell, env = shellenv.get_env() |
| if 'GOPATH' in env: |
| del env['GOPATH'] |
| with GolangBuildMock(shell=shell, env=env): |
| |
| file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go') |
| |
| def _run_build(view, result_queue): |
| notify_user('Press the "Open Documentation" button when prompted about GOPATH not being set') |
| |
| view.window().run_command('golang_build') |
| |
| custom_view_settings = VIEW_SETTINGS.copy() |
| del custom_view_settings['GOPATH'] |
| open_file(file_path, custom_view_settings, _run_build) |
| time.sleep(0.5) |
| self.assertTrue(confirm_user('Were you prompted that GOPATH was not set?')) |
| self.assertTrue(confirm_user('When you pressed "Open Documentation", was it opened in your browser?')) |
| |
| |
| def ensure_not_ui_thread(): |
| """ |
| The tests won't function properly if they are run in the UI thread, so |
| this functions throws an exception if that is attempted |
| """ |
| |
| if isinstance(threading.current_thread(), threading._MainThread): |
| raise RuntimeError('Tests can not be run in the UI thread') |
| |
| |
| def open_file(file_path, view_settings, callback): |
| """ |
| Open a file in Sublime Text, sets settings on the view and then executes |
| the callback once the file is opened |
| |
| :param file_path: |
| A unicode string of the path to the file to open |
| |
| :param view_settings: |
| A dict of settings to set the "golang" key of the view's settings to |
| |
| :param callback: |
| The callback to execute in the UI thread once the file is opened |
| """ |
| |
| result_queue = Queue() |
| file_param = file_path |
| if sys.platform == 'win32': |
| file_param = re.sub('^([a-zA-Z]):', '/\\1', file_param) |
| file_param = file_param.replace('\\', '/') |
| |
| def open_file_callback(): |
| window = sublime.active_window() |
| |
| window.run_command( |
| 'open_file', |
| { |
| 'file': file_param |
| } |
| ) |
| |
| when_file_opened(window, file_path, view_settings, callback, result_queue) |
| sublime.set_timeout(open_file_callback, 50) |
| return result_queue |
| |
| |
| def when_file_opened(window, file_path, view_settings, callback, result_queue): |
| """ |
| Periodic polling callback used by open_file() to find the newly-opened file |
| |
| :param window: |
| The sublime.Window to look for the view in |
| |
| :param file_path: |
| The file path of the file that was opened |
| |
| :param view_settings: |
| A dict of settings to set to the view's "golang" setting key |
| |
| :param callback: |
| The callback to execute when the file is opened |
| |
| :param result_queue: |
| A Queue() object the callback can use to communicate with the test |
| """ |
| |
| view = window.active_view() |
| if view and view.file_name() == file_path: |
| view.settings().set('golang', view_settings) |
| callback(view, result_queue) |
| return |
| # If the view was not ready, retry a short while later |
| sublime.set_timeout(lambda: when_file_opened(window, file_path, view_settings, callback, result_queue), 50) |
| |
| |
| def wait_build(result_queue, timeout=5): |
| """ |
| Uses the result queue to wait for a result from the open_file() callback |
| |
| :param result_queue: |
| The Queue() to get the result from |
| |
| :param timeout: |
| How long to wait before considering the test a failure |
| |
| :return: |
| The value from the queue |
| """ |
| |
| def _send_result(package_name, event_name, payload): |
| result_queue.put(payload.result) |
| |
| try: |
| package_events.listen('Golang Build', _send_result) |
| return result_queue.get(timeout=timeout) |
| finally: |
| package_events.unlisten('Golang Build', _send_result) |
| |
| |
| def confirm_user(message): |
| """ |
| Prompts the user to via a dialog to confirm a question |
| |
| :param message: |
| A unicode string of the message to present to the user |
| |
| :return: |
| A boolean - if the user clicked "Yes" |
| """ |
| |
| queue = Queue() |
| |
| def _show_ok_cancel(): |
| response = sublime.ok_cancel_dialog('Test Suite for Golang Build\n\n' + message, 'Yes') |
| queue.put(response) |
| |
| sublime.set_timeout(_show_ok_cancel, 1) |
| return queue.get() |
| |
| |
| def notify_user(message): |
| """ |
| Open a dialog for the user to inform them of a user interaction that is |
| part of the test suite |
| |
| :param message: |
| A unicode string of the message to present to the user |
| """ |
| |
| sublime.ok_cancel_dialog('Test Suite for Golang Build\n\n' + message, 'Ok') |