Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ Here you can find the recent changes to tmuxp.
CURRENT
-------

- [tests]: New context manager for tests, ``temp_session``.
- [tests]: New testsuite, ``testsuite.test_utils`` for testing testsuite
tools.
- [config] [builder]: New command, ``before_script``, which is a file to
be executed with a return code. It can be a bash, perl, python etc.
script.
- [docs]: :ref:`python_api_quickstart` per `Issue #56`_.

.. _Issue #56: https://github.com/tony/tmuxp/issues/56
Expand Down
48 changes: 48 additions & 0 deletions doc/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,54 @@ JSON
.. literalinclude:: ../.tmuxp.json
:language: json

Run script before launch
------------------------

You can use ``before_script`` to run a script before the tmux session
starts building.

It works by using the `Exit Status`_ code returned by a script. Your
script can be any type, including bash, python, ruby, etc.

A successful script will exit with a status of ``0``.

You can use this for things like bootstrapping ruby / python environments
for a project (or checking to verify their installation).

Run a python script (and check for it's return code), the script is
*relative to the ``.tmuxp.yaml``'s root* (Windows and panes omitted in
this example):

.. code-block:: yaml

session_name: my session
before_script: bootstrap.py
# ... the rest of your config

.. code-block:: json

{
"session_name": "my session",
"before_script": "bootstrap.py"
}

Run a shell script + check for return code on an absolute path. (Windows
and panes omitted in this example)

.. code-block:: yaml
session_name: another example
before_script: /absolute/path/this.sh # abs path to shell script
# ... the rest of your config

.. code-block:: json

{
"session_name": "my session",
"before_script": "/absolute/path/this.sh"
}

.. _Exit Status: http://tldp.org/LDP/abs/html/exit-status.html

Project configs
---------------

Expand Down
4 changes: 4 additions & 0 deletions tmuxp/testsuite/fixtures/script_complete.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

echo hello
echo $? # Exit status 0 returned because command executed successfully.
4 changes: 4 additions & 0 deletions tmuxp/testsuite/fixtures/script_failed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

exit 113 # Will return 113 to shell.
# To verify this, type "echo $?" after script terminates.
33 changes: 28 additions & 5 deletions tmuxp/testsuite/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
with_statement, unicode_literals

import time
from random import randint
import logging
import contextlib

try:
import unittest2 as unittest
except ImportError: # Python 2.7
import unittest

from random import randint

from . import t
from .. import Server, log, exc

Expand All @@ -25,6 +28,28 @@
TEST_SESSION_PREFIX = 'tmuxp_'


def get_test_session_name(server, prefix='tmuxp_'):
while True:
session_name = prefix + str(randint(0, 9999999))
if not t.has_session(session_name):
break
return session_name


@contextlib.contextmanager
def temp_session(server, session_name=None):
if not session_name:
session_name = get_test_session_name(server)

session = server.new_session(session_name)
try:
yield session
finally:
if server.has_session(session_name):
session.kill_session()
return


class TestCase(unittest.TestCase):

"""Base TestClass so we don't have to try: unittest2 every module. """
Expand Down Expand Up @@ -80,10 +105,7 @@ def bootstrap(self):
)
]

while True:
TEST_SESSION_NAME = TEST_SESSION_PREFIX + str(randint(0, 9999999))
if not t.has_session(TEST_SESSION_NAME):
break
TEST_SESSION_NAME = get_test_session_name(server=t)

try:
session = t.new_session(
Expand Down Expand Up @@ -113,4 +135,5 @@ def bootstrap(self):
assert TEST_SESSION_NAME != 'tmuxp'

self.TEST_SESSION_NAME = TEST_SESSION_NAME
self.server = t
self.session = session
44 changes: 44 additions & 0 deletions tmuxp/testsuite/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
"""Tests for tmuxp testsuite's helper and utility functions."""

from __future__ import absolute_import, division, print_function, \
with_statement, unicode_literals

from .helpers import get_test_session_name, temp_session, TestCase, \
TmuxTestCase, unittest


class TempSession(TmuxTestCase):

def test_kills_session(self):
server = self.server
session_name = get_test_session_name(server=server)

with temp_session(server=server, session_name=session_name) as session:
result = server.has_session(session_name)
self.assertTrue(result)

self.assertFalse(server.has_session(session_name))

def test_if_session_killed_before(self):
"""Handles situation where session already closed within context"""

server = self.server
session_name = get_test_session_name(server=server)

with temp_session(server=server, session_name=session_name) as session:

# an error or an exception within a temp_session kills the session
server.kill_session(session_name)

result = server.has_session(session_name)
self.assertFalse(result)

# really dead?
self.assertFalse(server.has_session(session_name))


def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TempSession))
return suite
131 changes: 129 additions & 2 deletions tmuxp/testsuite/workspacebuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@
import sys
import logging
import unittest
import subprocess
import time

import kaptan

from .. import Window, config, exc
from .._compat import text_type
from ..workspacebuilder import WorkspaceBuilder
from .helpers import TmuxTestCase
from .helpers import TestCase, TmuxTestCase, temp_session

logger = logging.getLogger(__name__)

current_dir = os.path.abspath(os.path.dirname(__file__))
example_dir = os.path.abspath(os.path.join(current_dir, '..', '..', 'examples'))
fixtures_dir = os.path.abspath(os.path.join(current_dir, 'fixtures'))


class TwoPaneTest(TmuxTestCase):
Expand Down Expand Up @@ -339,7 +341,7 @@ def test_blank_pane_count(self):
test_config = kaptan.Kaptan().import_config(self.yaml_config_file).get()
test_config = config.expand(test_config)
# for window in test_config['windows']:
# window['layout'] = 'tiled'
# window['layout'] = 'tiled'
builder = WorkspaceBuilder(sconf=test_config)
builder.build(session=self.session)

Expand Down Expand Up @@ -527,8 +529,133 @@ def test_window_index(self):
self.assertEqual(int(window['window_index']), expected_index)


class BeforeLoadScript(TmuxTestCase):

config_script_not_exists = """
session_name: sampleconfig
before_script: {fixtures_dir}/script_not_exists.sh
windows:
- panes:
- pane
"""

config_script_fails = """
session_name: sampleconfig
before_script: {fixtures_dir}/script_failed.sh
windows:
- panes:
- pane
"""

config_script_completes = """
session_name: sampleconfig
before_script: {fixtures_dir}/script_complete.sh
windows:
- panes:
- pane
"""

def test_throw_error_if_retcode_false(self):

sconfig = kaptan.Kaptan(handler='yaml')
yaml = self.config_script_fails.format(
fixtures_dir=fixtures_dir
)
sconfig = sconfig.import_config(yaml).get()
sconfig = config.expand(sconfig)
sconfig = config.trickle(sconfig)

builder = WorkspaceBuilder(sconf=sconfig)

with temp_session(self.server) as sess:
session_name = sess.get('session_name')

with self.assertRaises(subprocess.CalledProcessError):
builder.build(session=sess)

result = self.server.has_session(session_name)
self.assertFalse(
result,
msg="Kills session if before_script exits with errcode"
)

def test_throw_error_if_file_not_exists(self):

sconfig = kaptan.Kaptan(handler='yaml')
yaml = self.config_script_not_exists.format(
fixtures_dir=fixtures_dir
)
sconfig = sconfig.import_config(yaml).get()
sconfig = config.expand(sconfig)
sconfig = config.trickle(sconfig)

builder = WorkspaceBuilder(sconf=sconfig)

with temp_session(self.server) as sess:
session_name = sess.get('session_name')
temp_session_exists = self.server.has_session(sess.get('session_name'))
self.assertTrue(temp_session_exists)
with self.assertRaisesRegexp(
(BeforeLoadScriptNotExists, OSError),
'No such file or directory'
):
builder.build(session=sess)
result = self.server.has_session(session_name)
self.assertFalse(
result,
msg="Kills session if before_script doesn't exist"
)

def test_true_if_test_passes(self):

sconfig = kaptan.Kaptan(handler='yaml')
yaml = self.config_script_completes.format(
fixtures_dir=fixtures_dir
)
sconfig = sconfig.import_config(yaml).get()
sconfig = config.expand(sconfig)
sconfig = config.trickle(sconfig)

builder = WorkspaceBuilder(sconf=sconfig)

with temp_session(self.session.server) as session:
builder.build(session=self.session)


from ..workspacebuilder import run_before_script, BeforeLoadScriptNotExists, \
BeforeLoadScriptFailed


class RunBeforeScript(TestCase):

def test_raise_BeforeLoadScriptNotExists_if_not_exists(self):
script_file = os.path.join(fixtures_dir, 'script_noexists.sh')

with self.assertRaises(BeforeLoadScriptNotExists):
run_before_script(script_file)

with self.assertRaises(OSError):
run_before_script(script_file)

def test_raise_BeforeLoadScriptFailed_if_retcode(self):
script_file = os.path.join(fixtures_dir, 'script_failed.sh')

with self.assertRaises(BeforeLoadScriptFailed):
run_before_script(script_file)

with self.assertRaises(subprocess.CalledProcessError):
run_before_script(script_file)

def test_return_stdout_if_exits_zero(self):
script_file = os.path.join(fixtures_dir, 'script_complete.sh')

run_before_script(script_file)


def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(BeforeLoadScript))
suite.addTest(unittest.makeSuite(RunBeforeScript))
suite.addTest(unittest.makeSuite(BlankPaneTest))
suite.addTest(unittest.makeSuite(FocusAndPaneIndexTest))
suite.addTest(unittest.makeSuite(PaneOrderingTest))
Expand Down
Loading