Skip to content

Commit

Permalink
Added unit tests for Picatrix (#25)
Browse files Browse the repository at this point in the history
* Adding unit tests for the manager as well as a GH workflow.

* Adding unit tests for the common magics.

* adding networkx into dependencies

* Changing the issue templates

* Joining docker run operations together

* Update README.md
  • Loading branch information
kiddinn authored Nov 18, 2020
1 parent 01fadb7 commit 9a1bdd2
Show file tree
Hide file tree
Showing 16 changed files with 461 additions and 28 deletions.
16 changes: 0 additions & 16 deletions .github/ISSUE_TEMPLATE.md

This file was deleted.

32 changes: 32 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
21 changes: 21 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Feature request
assignees: kiddinn, bladyjoker, mariuszlitwin

---

**Describe the problem statement you are attempting to solve. Is the feature request related to
a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
9 changes: 6 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Fixes #<issue_number_goes_here>
This PR fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.
> Describe in a sentence or few what the PR accomplishes.
- [ ] Tests pass
- [ ] Appropriate changes to README are included in PR
- [ ] Unit tests added
- [ ] End-to-end tests added
- [ ] Appropriate changes to documentation is included
- [ ] If additional dependencies are needed, are they added into dependency files.
29 changes: 29 additions & 0 deletions .github/workflows/unittest-pipenv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: pipenv unittests

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: [3.7, 3.8]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install pipenv
pipenv install -d
pipenv install -r test_requirements.txt
- name: Test with pytest
run: |
pipenv run pytest
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Picatrix

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/picatrix/blob/main/notebooks/Quick_Primer_on_Colab_Jupyter.ipynb)
[![Open In Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/google/picatrix.git/main?filepath=notebooks%2F)
[![Version](https://img.shields.io/pypi/v/picatrix.svg)](https://pypi.python.org/pypi/picatrix)
![GitHub e2e Test Status](https://img.shields.io/github/workflow/status/google/picatrix/picatrix-end-to-end)

Picatrix is a framework that is meant to be used within a [Colab](https://colab.research.google.com) or
[Jupyter](https://jupyter.org/) notebooks. The framework is designed around
Expand Down
6 changes: 6 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ $ sudo docker-compose -f docker-build.yml build
$ sudo docker-compose -f docker-build.yml up -d
```

To build the container use:

```shell
$ sudo docker-compose --env-file config.env build
```

### Access Picatrix

To access picatrix you need to start a browser and paste in the following
Expand Down
1 change: 1 addition & 0 deletions picatrix/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
'ipywidgets>=5.1.1',
'jupyter>=1.0.0',
'jupyter-http-over-ws>=0.0.8',
'networkx>=2.5',
'MarkupSafe>=1.1.1',
'nest-asyncio>=1.4.1',
'notebook>=5.3.0',
Expand Down
43 changes: 43 additions & 0 deletions picatrix/lib/framework_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the picatrix framework."""
from typing import Optional
from typing import Text

from picatrix.lib import framework
from picatrix.lib import manager


def my_very_own_test_magic(data: Text, stuff: Optional[int] = 20) -> Text:
"""This is a magic that is used for testing.
Args:
data (str): This is a string.
stuff (int): And this is a number.
Returns:
str: A string that combines the two parameters together.
"""
return f'{data.strip()} - {stuff}'


def test_registration():
"""Test the magic decorator."""
magic = framework.picatrix_magic(my_very_own_test_magic)
assert magic.__doc__.startswith('usage: %my_very_own_test_magic')
results = magic(line='--stuff 23 this is a text')
assert results == 'this is a text - 23'

manager.MagicManager.deregister_magic(magic.magic_name)
13 changes: 8 additions & 5 deletions picatrix/lib/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@
class MagicManager:
"""Manager class for Picatrix magics."""

MAGICS_DF_COLUMNS = ['name', 'description', 'line', 'cell', 'function']
_magics: Dict[Text, Callable[[str, str], str]] = {}

@classmethod
def clear_magics(cls):
"""Clear all magic registration."""
cls._magics = {}
magics = list(cls._magics.keys())
for magic_name in magics:
cls.deregister_magic(magic_name)

@classmethod
def deregister_magic(cls, magic_name: str):
Expand Down Expand Up @@ -78,13 +81,13 @@ def get_magic(cls, magic_name: str) -> Callable[[str, str], str]:
return cls._magics.get(magic_name)

@classmethod
def get_magic_info(cls, as_pandas: bool = False) -> Union[
def get_magic_info(cls, as_pandas: bool = True) -> Union[
pandas.DataFrame, List[Tuple[str, str]]]:
"""Get a list of all magics.
Args:
as_pandas (bool): boolean to determine whether to receive the results
as a list of tuples or a pandas DataFrame. Defaults to False.
as a list of tuples or a pandas DataFrame. Defaults to True.
Returns:
Either a pandas DataFrame or a list of tuples, depending on the as_pandas
Expand All @@ -104,9 +107,9 @@ def get_magic_info(cls, as_pandas: bool = False) -> Union[
'function': f'{magic_name}_func',
'description': description}
entries.append(magic_dict)

df = pandas.DataFrame(entries)
return df[
['name', 'description', 'line', 'cell', 'function']].sort_values('name')
return df[cls.MAGICS_DF_COLUMNS].sort_values('name')

@classmethod
def register_magic(
Expand Down
112 changes: 112 additions & 0 deletions picatrix/lib/manager_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the pixatrix manager."""
import pytest
import mock

from picatrix.lib import manager
from picatrix.lib import utils


manager.get_ipython = mock.MagicMock()
utils.get_ipython = mock.MagicMock()


def test_registration():
"""Test registering a magic and getting a copy of it and de-registering."""
manager.MagicManager.clear_magics()

def my_magic(cell=None, line=None):
"""This is a magic."""
if not cell:
cell = 'foo'
if not line:
line = 'bar'
return f'{cell}{line}'

my_magic.magic_name = 'magical_function'
my_magic.fn = my_magic
manager.MagicManager.register_magic(my_magic)

magic_from_manager = manager.MagicManager.get_magic('magical_function')
assert magic_from_manager() == 'foobar'

my_magic.magic_name = 'other_magic'
def conditional():
return False

manager.MagicManager.register_magic(my_magic, conditional=conditional)
magic_from_manager = manager.MagicManager.get_magic('other_magic')
assert magic_from_manager is None

manager.MagicManager.register_magic(my_magic)
magic_from_manager = manager.MagicManager.get_magic('other_magic')
assert magic_from_manager() == 'foobar'

manager.MagicManager.deregister_magic('other_magic')
magic_from_manager = manager.MagicManager.get_magic('other_magic')
assert magic_from_manager is None

manager.MagicManager.deregister_magic('magical_function')
magic_from_manager = manager.MagicManager.get_magic('magical_function')
assert magic_from_manager is None

with pytest.raises(KeyError):
manager.MagicManager.deregister_magic('does_not_exist')


def test_magic_info():
"""Test the get_magic_info."""
# Start by clearing the current registration.
manager.MagicManager.clear_magics()
def magical_func():
"""This is a magical function that returns pure magic."""
return 'magic'

magical_func.magic_name = 'magical_function'
magical_func.fn = magical_func
manager.MagicManager.register_magic(magical_func)

def second_magic():
"""This is even more magic."""
return 'fab'
second_magic.magic_name = 'some_magic'
second_magic.fn = second_magic
manager.MagicManager.register_magic(second_magic)

def other_magic():
"""Could this be it?"""
return 'true magic'

other_magic.magic_name = 'other_magic'
other_magic.fn = other_magic
manager.MagicManager.register_magic(other_magic)

info_df = manager.MagicManager.get_magic_info(as_pandas=True)
assert len(info_df) == 3
assert not info_df[info_df.name == 'other_magic'].empty

desc_set = set(info_df.description.unique())
expected_set = set([
'Could this be it?', 'This is even more magic.',
'This is a magical function that returns pure magic.'])

assert desc_set == expected_set

entries = manager.MagicManager.get_magic_info(as_pandas=False)
assert len(entries) == 3
names = [x[0] for x in entries]
assert 'other_magic' in names
assert 'some_magic' in names
Loading

0 comments on commit 9a1bdd2

Please sign in to comment.