Reorganize tests making them easier to run

- The tests now automatically run depending on whether you have the dependencies
installed, instead of failing and throwing exceptions.
- CONTRIBUTING.md has more information on how to run the tests.
- When the tests run, they will save their logs to /test_logs instead
of printing them so you can read them later.
- Change names of source file directories to make them more descriptive
pull/8/head
Dan Puttick 2016-12-24 16:46:22 -05:00
parent 70a73dc292
commit 3dad4faa61
23 changed files with 201 additions and 103 deletions

View File

@ -61,7 +61,7 @@ install:
- pushd theZoo/malwares/Binaries
- python unpackall.py
- popd
- mv theZoo/malwares/Binaries/out tests/src_complex/
- mv theZoo/malwares/Binaries/out tests/src_invalid/
# Path traversal
- git clone https://github.com/jwilk/path-traversal-samples
- pushd path-traversal-samples
@ -72,12 +72,12 @@ install:
- make
- popd
- popd
- mv path-traversal-samples/zip/*.zip tests/src_complex/
- mv path-traversal-samples/rar/*.rar tests/src_complex/
- mv path-traversal-samples/zip/*.zip tests/src_invalid/
- mv path-traversal-samples/rar/*.rar tests/src_invalid/
# Office docs
- git clone https://github.com/eea/odfpy.git
- mv odfpy/tests/examples/* tests/src_complex/
- pushd tests/src_complex/
- mv odfpy/tests/examples/* tests/src_invalid/
- pushd tests/src_invalid/
- wget https://bitbucket.org/decalage/olefileio_pl/raw/3073963b640935134ed0da34906fea8e506460be/Tests/images/test-ole-file.doc
- wget --no-check-certificate https://www.officedissector.com/corpus/fraunhoferlibrary.zip
- unzip -o fraunhoferlibrary.zip

View File

@ -29,5 +29,13 @@ or if you have an example you'd like to contribute.
Running the tests
=================
* Running the tests is easy. First, make sure you've installed the project and testing dependencies.
Then, run `python -m pytest` or just `pytest` in the top level or /tests directory.
* Running the tests is fairly straightforward.
* First, make sure you've installed the project and testing dependencies.
* Then, run `python -m pytest` or just `pytest` in the top level directory of the module.
* Each integration test that runs will generate a timestamped copy of the log for that run
in the tests/testlogs directory.
* If you'd like to get information about code coverage, run the tests using
`pytest --cov=kittengroomer`.
* You can test with multiple versions of Python if you have them installed
by running `pip install tox` and then `tox`. Make sure you modify "envlist"
in tox.ini for the Python versions you plan to use.

View File

@ -15,6 +15,10 @@ Requirements per script
filecheck.py
------------
This is the script used by the [CIRCLean](https://github.com/CIRCL/Circlean)
USB key sanitizer. It is designed to handle a range of file types, and will
mark them as dangerous if they meet certain criteria.
Requirements by type of document:
* Microsoft office: oletools, olefile
* OOXML: officedissector
@ -23,12 +27,14 @@ Requirements by type of document:
* Metadata: exifread
* Images: pillow
Note: pdfid is a not installable with pip. It must be downloaded and installed
manually in the directory where filecheck will be run.
```
sudo apt-get install p7zip-full p7zip-rar libxml2-dev libxslt1-dev
pip install lxml oletools olefile pillow exifread
pip install git+https://github.com/Rafiot/officedissector.git
# pdfid is not a package, installing manually
# installing pdfid manually
wget https://didierstevens.com/files/software/pdfid_v0_2_1.zip
unzip pdfid_v0_2_1.zip
```
@ -36,6 +42,9 @@ Requirements by type of document:
generic.py
----------
This is a script used by an older version of CIRCLean. It has more dependencies
than filecheck.py and they are more complicated to install.
Requirements by type of document:
* Office and all text files: unoconv, libreoffice
* PDF: ghostscript, pdf2htmlEX
@ -60,9 +69,16 @@ Requirements by type of document:
pier9.py
--------
This script has a list of file formats for various brands of industrial
manufacturing equipment, such as 3d printers, CNC machines, etc. It only
copies files that match these file formats.
No external dependencies required.
specific.py
-----------
As the name suggests, this script copies only specific file formats according
to the configuration provided by the user.
No external dependencies required.

View File

@ -197,6 +197,7 @@ class KittenGroomerFileCheck(KittenGroomerBase):
def _print_log(self):
"""Print the logs related to the current file being processed."""
# TODO: change name to _write_log
tmp_log = self.log_name.fields(**self.cur_file.log_details)
if self.cur_file.is_dangerous():
tmp_log.warning(self.cur_file.log_string)

22
tests/logging.py Normal file
View File

@ -0,0 +1,22 @@
import os
def save_logs(groomer, test_description):
divider = ('=' * 10 + '{}' + '=' * 10 + '\n')
test_log_path = 'tests/test_logs/{}.log'.format(test_description)
with open(test_log_path, 'w+') as test_log:
test_log.write(divider.format('TEST LOG'))
with open(groomer.log_processing, 'r') as logfile:
log = logfile.read()
test_log.write(log)
if groomer.debug:
if os.path.exists(groomer.log_debug_err):
test_log.write(divider.format('ERR LOG'))
with open(groomer.log_debug_err, 'r') as debug_err:
err = debug_err.read()
test_log.write(err)
if os.path.exists(groomer.log_debug_out):
test_log.write(divider.format('OUT LOG'))
with open(groomer.log_debug_out, 'r') as debug_out:
out = debug_out.read()
test_log.write(out)

Binary file not shown.

Binary file not shown.

View File

@ -1,84 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import pytest
from bin.specific import KittenGroomerSpec
from bin.pier9 import KittenGroomerPier9
from bin.generic import KittenGroomer
from bin.filecheck import KittenGroomerFileCheck
skip = pytest.mark.skip
py2_only = pytest.mark.skipif(sys.version_info.major == 3,
reason="filecheck.py only runs on python 2")
@pytest.fixture
def src_simple():
return os.path.join(os.getcwd(), 'tests/src_simple')
@pytest.fixture
def src_complex():
return os.path.join(os.getcwd(), 'tests/src_complex')
@pytest.fixture
def dst():
return os.path.join(os.getcwd(), 'tests/dst')
def test_specific_valid(src_simple, dst):
spec = KittenGroomerSpec(src_simple, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_specific_invalid(src_complex, dst):
spec = KittenGroomerSpec(src_complex, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_pier9(src_complex, dst):
spec = KittenGroomerPier9(src_complex, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_generic(src_simple, dst):
spec = KittenGroomer(src_simple, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_generic_2(src_complex, dst):
spec = KittenGroomer(src_complex, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_filecheck(src_complex, dst):
spec = KittenGroomerFileCheck(src_complex, dst, debug=True)
spec.processdir()
dump_logs(spec)
def test_filecheck_2(src_simple, dst):
spec = KittenGroomerFileCheck(src_simple, dst, debug=True)
spec.processdir()
dump_logs(spec)
## Helper functions
def dump_logs(spec):
print(open(spec.log_processing, 'rb').read())
if spec.debug:
if os.path.exists(spec.log_debug_err):
print(open(spec.log_debug_err, 'rb').read())
if os.path.exists(spec.log_debug_out):
print(open(spec.log_debug_out, 'rb').read())

View File

@ -1,16 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import pytest
from bin.filecheck import KittenGroomerFileCheck, File, main
from tests.logging import save_logs
try:
from bin.filecheck import KittenGroomerFileCheck, File, main
NODEPS = False
except ImportError:
NODEPS = True
skipif_nodeps = pytest.mark.skipif(NODEPS,
reason="Dependencies aren't installed")
@skipif_nodeps
class TestIntegration:
@pytest.fixture
def src_valid(self):
return os.path.join(os.getcwd(), 'tests/src_valid')
@pytest.fixture
def src_invalid(self):
return os.path.join(os.getcwd(), 'tests/src_invalid')
@pytest.fixture
def dst(self):
return os.path.join(os.getcwd(), 'tests/dst')
def test_filecheck(self, src_invalid, dst):
groomer = KittenGroomerFileCheck(src_invalid, dst, debug=True)
groomer.processdir()
test_description = "filecheck_invalid"
save_logs(groomer, test_description)
def test_filecheck_2(self, src_valid, dst):
groomer = KittenGroomerFileCheck(src_valid, dst, debug=True)
groomer.processdir()
test_description = "filecheck_valid"
save_logs(groomer, test_description)
class TestFileHandling:
pass
# We're going to give KittenGroomer a bunch of files, and it's going to process them
# Maybe we want to make a function that processdir delegates to? Or is it just the File Object that's responsible?
# Ideally we should be able to pass a path to a function and have it do stuff? And then we can test that function?
# So we have a function that takes a path and returns...log info? That makes sense actually. Or some sort of meta data
# The function could maybe be called processfile

50
tests/test_generic.py Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import pytest
from bin.generic import KittenGroomer, File, main
from tests.logging import save_logs
skipif_nodeps = pytest.mark.skipif(os.path.exists('/usr/bin/unoconv') is False,
reason="Dependencies aren't installed")
@skipif_nodeps
class TestIntegration:
@pytest.fixture
def src_valid(self):
return os.path.join(os.getcwd(), 'tests/src_valid')
@pytest.fixture
def src_invalid(self):
return os.path.join(os.getcwd(), 'tests/src_invalid')
@pytest.fixture
def dst(self):
return os.path.join(os.getcwd(), 'tests/dst')
def test_generic(self, src_valid, dst):
groomer = KittenGroomer(src_valid, dst, debug=True)
groomer.processdir()
test_description = 'generic_valid'
save_logs(groomer, test_description)
def test_generic_2(self, src_invalid, dst):
groomer = KittenGroomer(src_invalid, dst, debug=True)
groomer.processdir()
test_description = 'generic_invalid'
save_logs(groomer, test_description)
class TestFileHandling:
pass
# We're going to give KittenGroomer a bunch of files, and it's going to process them
# Maybe we want to make a function that processdir delegates to? Or is it just the File Object that's responsible?
# Ideally we should be able to pass a path to a function and have it do stuff? And then we can test that function?
# So we have a function that takes a path and returns...log info? That makes sense actually. Or some sort of meta data
# The function could maybe be called processfile

View File

@ -21,7 +21,7 @@ class TestFileBase:
@fixture
def source_file(self):
return 'tests/src_simple/blah.conf'
return 'tests/src_valid/blah.conf'
@fixture
def dest_file(self):
@ -84,7 +84,7 @@ class TestFileBase:
# We should probably catch everytime that happens and tell the user explicitly happened (and maybe put it in the log)
def test_create(self):
file = FileBase('tests/src_simple/blah.conf', '/tests/dst/blah.conf')
file = FileBase('tests/src_valid/blah.conf', '/tests/dst/blah.conf')
def test_create_broken(self, tmpdir):
with pytest.raises(TypeError):
@ -221,7 +221,7 @@ class TestKittenGroomerBase:
@fixture
def source_directory(self):
return 'tests/src_complex'
return 'tests/src_invalid'
@fixture
def dest_directory(self):

0
tests/test_logs/.keepdir Normal file
View File

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import pytest
from bin.specific import KittenGroomerSpec
from bin.pier9 import KittenGroomerPier9
from tests.logging import save_logs
@pytest.fixture
def src_valid():
return os.path.join(os.getcwd(), 'tests/src_valid')
@pytest.fixture
def src_invalid():
return os.path.join(os.getcwd(), 'tests/src_invalid')
@pytest.fixture
def dst():
return os.path.join(os.getcwd(), 'tests/dst')
def test_specific_valid(src_valid, dst):
groomer = KittenGroomerSpec(src_valid, dst, debug=True)
groomer.processdir()
test_description = 'specific_valid'
save_logs(groomer, test_description)
def test_specific_invalid(src_invalid, dst):
groomer = KittenGroomerSpec(src_invalid, dst, debug=True)
groomer.processdir()
test_description = 'specific_invalid'
save_logs(groomer, test_description)
def test_pier9_valid(src_invalid, dst):
groomer = KittenGroomerPier9(src_invalid, dst, debug=True)
groomer.processdir()
test_description = 'pier9_valid'
save_logs(groomer, test_description)
def test_pier9_invalid(src_invalid, dst):
groomer = KittenGroomerPier9(src_invalid, dst, debug=True)
groomer.processdir()
test_description = 'pier9_invalid'
save_logs(groomer, test_description)

View File

@ -2,4 +2,4 @@
envlist=py27,py35
[testenv]
deps=-rdev-requirements.txt
commands= pytest tests/test_helpers.py --cov=kittengroomer
commands= pytest --cov=kittengroomer --cov=bin