blob: b37e1b029b8e03759b1f81dab248ba2cb215eed9 [file] [log] [blame]
# 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')