Skip to content

Commit

Permalink
Update CI/tests to allow running proper, realistic unit tests (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
andersy005 authored Feb 17, 2022
1 parent 959f2c7 commit 917c142
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 31 deletions.
13 changes: 13 additions & 0 deletions .github/scripts/ssh_setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

ssh-keygen -t ed25519 -f ~/.ssh/my_key -N ''
cat > ~/.ssh/config <<EOF
Host eniac.local
User $USER
HostName 127.0.0.1
IdentityFile ~/.ssh/my_key
EOF
echo -n 'from="127.0.0.1" ' | cat - ~/.ssh/my_key.pub > ~/.ssh/authorized_keys
chmod og-rw ~
ls -la ~/.ssh
ssh -o 'StrictHostKeyChecking no' eniac.local id
7 changes: 7 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: CI
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '0 0 * * *' # Daily “At 00:00”
Expand All @@ -24,6 +26,11 @@ jobs:
python-version: ['3.7', '3.8', '3.9', '3.10']
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Configure SSH key for localhost
run: |
.github/scripts/ssh_setup.sh
- uses: conda-incubator/setup-miniconda@v2
with:
channels: conda-forge
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/upstream-dev-ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Upstream CI
on:
push:
branches:
- main
schedule:
- cron: '0 0 * * *' # Daily “At 00:00” UTC
workflow_dispatch: # allows you to trigger the workflow run manually
Expand All @@ -22,6 +24,11 @@ jobs:
python-version: ['3.10']
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Configure SSH key for localhost
run: |
.github/scripts/ssh_setup.sh
- uses: conda-incubator/setup-miniconda@v2
id: conda
with:
Expand Down Expand Up @@ -49,6 +56,7 @@ jobs:
- name: Report Status
if: |
always()
&& github.ref == 'refs/heads/main'
&& (steps.conda.outcome != 'success' || steps.install.outcome != 'success' || steps.install.outcome != 'success')
uses: actions/github-script@v6
with:
Expand Down
67 changes: 37 additions & 30 deletions jupyter_forward/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from fabric import Connection
from rich.console import Console

timestamp = datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S')

console = Console()


Expand Down Expand Up @@ -178,29 +180,8 @@ def _launch_jupyter(self):

else:
self.run_command(check_jupyter_status)
console.rule(
f'[bold green] Checking $TMPDIR and $HOME on {self.session.host}', characters='*'
)
tmp_dir_env_status = self.run_command(command='printenv TMPDIR', exit=False)
home_dir_env_status = self.run_command(command='printenv HOME', exit=False)
check_dir_command = 'touch ${}/foobar && rm -rf ${}/foobar && echo "${} is WRITABLE" || echo "${} is NOT WRITABLE"'
if not tmp_dir_env_status.failed:
self._check_log_file_dir(check_dir_command, 'TMPDIR', '$TMPDIR')
elif not home_dir_env_status.failed:
self._check_log_file_dir(check_dir_command, 'HOME', '$HOME')
else:
tmp_dir_error_message = '$TMPDIR is not defined'
home_dir_error_message = '$HOME is not defined'
console.print(
f'[bold red]Can not determine directory for log file:\n{home_dir_error_message}\n{tmp_dir_error_message}'
)
sys.exit(1)
self.log_dir = f'{self.log_dir}/.jupyter_forward'
self.run_command(command=f'mkdir -p {self.log_dir}')
timestamp = datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S')
self.log_file = f'{self.log_dir}/log.{timestamp}'
self.run_command(command=f'touch {self.log_file}')

self._set_log_directory()
self._set_log_file()
command = r'jupyter lab --no-browser --ip=\$(hostname -f)'
if self.notebook_dir:
command = f'{command} --notebook-dir={self.notebook_dir}'
Expand Down Expand Up @@ -243,13 +224,39 @@ def _launch_jupyter(self):
open_browser(url=self.parsed_result['url'], path=self.notebook)
self.run_command(command=f'tail -f {self.log_file}')

def _check_log_file_dir(self, check_dir_command, arg1, arg2):
_tmp_dir_status = self.run_command(
command=check_dir_command.format(arg1, arg1, arg1, arg1), exit=False
)

if 'is WRITABLE' in _tmp_dir_status.stdout.strip():
self.log_dir = arg2
def _set_log_file(self):
log_file = f'{self.log_dir}/log_{timestamp}.txt'
self.run_command(command=f'touch {log_file}')
self.log_file = log_file
console.print(f'[bold cyan]:white_check_mark: Log file is set to {log_file}')
return self

def _set_log_directory(self):
def _check_log_file_dir(directory):
check_dir_command = f'touch {directory}/foobar && rm -rf {directory}/foobar && echo "{directory} is WRITABLE" || echo "{directory} is NOT WRITABLE"'
_tmp_dir_status = self.run_command(command=check_dir_command, exit=False)
return directory if 'is WRITABLE' in _tmp_dir_status.stdout.strip() else None

console.rule(f'[bold green] Creating log file on {self.session.host}', characters='*')
log_dir = None
tmp_dir_env_status = self.run_command(command='printenv TMPDIR', exit=False)
home_dir_env_status = self.run_command(command='printenv HOME', exit=False)
if not tmp_dir_env_status.failed:
log_dir = _check_log_file_dir('$TMPDIR')
elif not home_dir_env_status.failed:
log_dir = _check_log_file_dir('$HOME')
else:
tmp_dir_error_message = '$TMPDIR is not defined'
home_dir_error_message = '$HOME is not defined'
console.print(
f'[bold red]Can not determine directory for log file:\n{home_dir_error_message}\n{tmp_dir_error_message}'
)
sys.exit(1)
log_dir = f'{log_dir}/.jupyter_forward'
self.run_command(command=f'mkdir -p {log_dir}')
console.print(f'[bold cyan]:white_check_mark: Log directory is set to {log_dir}')
self.log_dir = log_dir
return self


def open_browser(port: int = None, token: str = None, url: str = None, path=None):
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ skip=

[tool:pytest]
console_output_style = count
addopts = --cov=./ --cov-report=xml --verbose
addopts = --cov=./ --cov-report=xml --verbose -s
49 changes: 49 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import datetime
import os
import socket
from unittest import mock as mock

import pytest

import jupyter_forward
from jupyter_forward.core import is_port_available, open_browser, parse_stdout

NOT_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS') is None
requires_gha = pytest.mark.skipif(NOT_GITHUB_ACTIONS, reason='requires GITHUB_ACTIONS')


@pytest.fixture(scope='session', params=[f"{os.environ['USER']}@eniac.local"])
def runner(request):
remote = jupyter_forward.RemoteRunner(request.param)
yield remote
remote.close()


@pytest.mark.parametrize(
'stdout, expected',
Expand Down Expand Up @@ -68,3 +81,39 @@ def test_open_browser(port, token, url, expected):
with mock.patch('webbrowser.open') as mockwebopen:
open_browser(port, token, url)
mockwebopen.assert_called_once_with(expected, new=2)


@requires_gha
def test_connection(runner):
USER = os.environ['USER']
assert runner.session.is_connected
assert runner.session.host == '127.0.0.1'
assert runner.session.user == USER


@requires_gha
@pytest.mark.parametrize('command', ['echo $HOME'])
def test_run_command(runner, command):
out = runner.run_command(command)
assert not out.failed
assert out.stdout.strip() == f"{os.environ['HOME']}"


@requires_gha
@pytest.mark.parametrize('command', ['echod $HOME'])
def test_run_command_failure(runner, command):
out = runner.run_command(command, exit=False)
assert out.failed
assert 'echod: command not found' in out.stdout.strip()

with pytest.raises(SystemExit):
runner.run_command(command)


@requires_gha
def test_set_logs(runner):
runner._set_log_directory()
assert runner.log_dir == '$HOME/.jupyter_forward'
runner._set_log_file()
now = datetime.datetime.now()
assert f"log_{now.strftime('%Y-%m-%dT%H')}" in runner.log_file

0 comments on commit 917c142

Please sign in to comment.