Start introducing the framework for a test suite
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Olivier 'reivilibre' 2021-07-18 21:44:49 +01:00
parent 7560aefdd0
commit aa5eae8424
8 changed files with 266 additions and 2 deletions

4
.gitignore vendored
View File

@ -5,5 +5,5 @@
/yama.iml
/.project
/.gdb_history
/testsuite/.idea
/testsuite/venv

18
testsuite/README.md Normal file
View File

@ -0,0 +1,18 @@
# yama and datman test suite
To run the tests in this suite, you shall require Python 3.8+.
In a virtual environment:
`pip install .`
``
## Development instructions
`pip install -e .[dev]`
To run linter checks, run `./scripts-dev/lint.sh`.

View File

View File

@ -0,0 +1,6 @@
from unittest import TestCase
class TestBackupAndExtract(TestCase):
def test_backup_one_full(self):
pass

View File

@ -0,0 +1,104 @@
import os.path
from hashlib import sha256
from pathlib import Path
from random import Random
from typing import Union, Tuple
import attr
from immutabledict import immutabledict
MIN_FILE_SIZE = 0
MAX_FILE_SIZE = 64 * 1024 * 1024
CHUNK_SIZE = 4096
ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789"
@attr.s(auto_attribs=True, frozen=True)
class FileDescriptor:
sha256_sum: str
mtime_ms: int
permissions: int
owner: int
group: int
@attr.s(auto_attribs=True, frozen=True)
class DirectoryDescriptor:
contents: immutabledict[str, Union[FileDescriptor, "DirectoryDescriptor"]]
mtime_ms: int
permissions: int
owner: int
group: int
def generate_random_file(rng: Random, path: Path) -> FileDescriptor:
"""
Generates a random file at the given path, and returns its descriptor.
:param rng: PRNG to use
:param path: path to use
:return: sha256 hex string
"""
file_size = rng.randint(MIN_FILE_SIZE, MAX_FILE_SIZE)
bytes_to_gen = file_size
sha256_hasher = sha256()
with path.open('wb') as file:
while bytes_to_gen > CHUNK_SIZE:
next_bytes = rng.randbytes(CHUNK_SIZE)
file.write(next_bytes)
sha256_hasher.update(next_bytes)
bytes_to_gen -= CHUNK_SIZE
next_bytes = rng.randbytes(bytes_to_gen)
file.write(next_bytes)
sha256_hasher.update(next_bytes)
final_sha256 = sha256_hasher.hexdigest()
file_stat = os.stat(path)
return FileDescriptor(
final_sha256,
file_stat.st_mtime_ns // 1000000,
file_stat.st_mode,
file_stat.st_uid,
file_stat.st_gid
)
def generate_random_dir(rng: Random, path: Path, max_remaining_files: int) -> Tuple[DirectoryDescriptor, int]:
"""
Generates a random directory at the given path, and returns its descriptor (and the remaining number of files allowed).
:param rng: PRNG to use
:param path: path to use
:param max_remaining_files: The maximum number of files allowed.
:return: (descriptor, number of files allowed remaining)
"""
os.mkdir(path)
num_files = rng.randint(0, max_remaining_files)
max_remaining_files -= num_files
contents = dict()
for _ in range(num_files):
filename_len = rng.randint(4, 16)
filename = "".join(rng.choice(ALPHABET) for _ in range(filename_len))
filepath = path.joinpath(filename)
is_file = rng.choice((True, False))
if is_file:
contents[filename] = generate_random_file(rng, filepath)
else:
contents[filename], max_remaining_files = generate_random_dir(rng, filepath, max_remaining_files)
file_stat = os.stat(path)
return DirectoryDescriptor(
immutabledict(contents),
file_stat.st_mtime_ns // 1000000,
file_stat.st_mode,
file_stat.st_uid,
file_stat.st_gid
), max_remaining_files

130
testsuite/setup.py Normal file
View File

@ -0,0 +1,130 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import os
import sys
from shutil import rmtree
from setuptools import find_packages, setup, Command
# Package meta-data.
NAME = "yamadatmantestsuite"
DESCRIPTION = "test suite for yama and datman"
URL = "https://bics.ga/reivilibre/yama"
EMAIL = "reivi@librepush.net"
AUTHOR = "Olivier 'reivilibre'"
REQUIRES_PYTHON = ">=3.8.0"
VERSION = "0.0.0"
# What packages are required for this module to be executed?
REQUIRED = [
"green",
"attrs",
"immutabledict"
]
# What packages are optional?
EXTRAS = {
"dev": [
"black==21.7b0",
"flake8==3.9.2",
"isort==5.9.2"
]
}
# The rest you shouldn't have to touch too much :)
# ------------------------------------------------
# Except, perhaps the License and Trove Classifiers!
# If you do change the License, remember to change the Trove Classifier for that!
here = os.path.abspath(os.path.dirname(__file__))
# Import the README and use it as the long-description.
# Note: this will only work if 'README.md' is present in your MANIFEST.in file!
try:
with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f:
long_description = "\n" + f.read()
except FileNotFoundError:
long_description = DESCRIPTION
# Load the package's __version__.py module as a dictionary.
about = {}
if not VERSION:
project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
with open(os.path.join(here, project_slug, "__version__.py")) as f:
exec(f.read(), about)
else:
about["__version__"] = VERSION
class UploadCommand(Command):
"""Support setup.py upload."""
description = "Build and publish the package."
user_options = []
@staticmethod
def status(s):
"""Prints things in bold."""
print("\033[1m{0}\033[0m".format(s))
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
try:
self.status("Removing previous builds…")
rmtree(os.path.join(here, "dist"))
except OSError:
pass
self.status("Building Source and Wheel (universal) distribution…")
os.system("{0} setup.py sdist bdist_wheel --universal".format(sys.executable))
self.status("Uploading the package to PyPI via Twine…")
os.system("twine upload dist/*")
self.status("Pushing git tags…")
os.system("git tag v{0}".format(about["__version__"]))
os.system("git push --tags")
sys.exit()
# Where the magic happens:
setup(
name=NAME,
version=about["__version__"],
description=DESCRIPTION,
long_description=long_description,
long_description_content_type="text/markdown",
author=AUTHOR,
author_email=EMAIL,
python_requires=REQUIRES_PYTHON,
url=URL,
packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
# If your package is a single module, use this instead of 'packages':
# py_modules=['mypackage'],
entry_points={
"console_scripts": [
"phototrie=phototrie.phototrie:cli",
"batchrename=phototrie.batchrename:cli",
"datename=phototrie.datename:cli",
],
},
install_requires=REQUIRED,
extras_require=EXTRAS,
include_package_data=True,
# TODO license='GPL3',
classifiers=[
# Trove classifiers
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
"Programming Language :: Python",
"Programming Language :: Python :: 3",
],
)

View File

View File

@ -0,0 +1,6 @@
from unittest import TestCase
# class TestStoreAndRetrieve(TestCase):
# def test_store_and_retrieve(self):
#