Skip to content

Commit

Permalink
tests for the utils (#12)
Browse files Browse the repository at this point in the history
* remove the unused `ComplexAdapter` class

* add tests for the untested `Factor`

* add a test for the datetime converter

* fix a off-by-one error in the datetime converter

* add tests for the second datetime constructor

* add a test for the metadata injector

* convert the ascii integer function into the adapter's constructor

* strip ascii integers on both ends of the string

* don't save the number of bytes on the adapter

* same for padded string, and ascii float and ascii complex

* verify that all adapters do not implement `_encode`

* add tests for `StripNullBytes`

* configure `coverage.py`

* trigger the translator's validator

* ignore the fallback import in coverage reports

* add tests for `itemsplit`

* add tests for `keysplit` and `valsplit`
  • Loading branch information
keewis committed Jul 6, 2023
1 parent dca9a39 commit 56be716
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 39 deletions.
64 changes: 28 additions & 36 deletions ceos_alos2/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
from construct import Struct


class AsciiIntegerAdapter(Adapter):
class AsciiInteger(Adapter):
def __init__(self, n_bytes):
base = PaddedString_(n_bytes, "ascii")
super().__init__(base)

def _decode(self, obj, context, path):
stripped = obj.rstrip()
stripped = obj.strip()
if not stripped:
return -1
return int(stripped)
Expand All @@ -16,9 +20,13 @@ def _encode(self, obj, context, path):
raise NotImplementedError


class AsciiFloatAdapter(Adapter):
class AsciiFloat(Adapter):
def __init__(self, n_bytes):
base = PaddedString_(n_bytes, "ascii")
super().__init__(base)

def _decode(self, obj, context, path):
stripped = obj.rstrip()
stripped = obj.strip()
if not stripped:
stripped = "nan"

Expand All @@ -28,43 +36,33 @@ def _encode(self, obj, context, path):
raise NotImplementedError


class AsciiComplexAdapter(Adapter):
class AsciiComplex(Adapter):
def __init__(self, n_bytes):
base = Struct(
"real" / AsciiFloat(n_bytes // 2),
"imaginary" / AsciiFloat(n_bytes // 2),
)
super().__init__(base)

def _decode(self, obj, context, path):
return obj.real + 1j * obj.imaginary

def _encode(self, obj, context, path):
raise NotImplementedError


class PaddedStringAdapter(Adapter):
class PaddedString(Adapter):
def __init__(self, n_bytes):
base = PaddedString_(n_bytes, "ascii")
super().__init__(base)

def _decode(self, obj, context, path):
return obj.strip()

def _encode(self, obj, context, path):
raise NotImplementedError


def AsciiInteger(n_bytes):
return AsciiIntegerAdapter(PaddedString_(n_bytes, "ascii"))


def AsciiFloat(n_bytes):
return AsciiFloatAdapter(PaddedString_(n_bytes, "ascii"))


def PaddedString(n_bytes):
return PaddedStringAdapter(PaddedString_(n_bytes, "ascii"))


def AsciiComplex(n_bytes):
obj = Struct(
"real" / AsciiFloat(n_bytes // 2),
"imaginary" / AsciiFloat(n_bytes // 2),
)

return AsciiComplexAdapter(obj)


class Factor(Adapter):
def __init__(self, obj, factor):
super().__init__(obj)
Expand Down Expand Up @@ -101,7 +99,9 @@ def _encode(self, obj, context, path):
class DatetimeYdms(Adapter):
def _decode(self, obj, context, path):
base = datetime.datetime(obj["year"], 1, 1)
timedelta = datetime.timedelta(days=obj["day_of_year"], milliseconds=obj["milliseconds"])
timedelta = datetime.timedelta(
days=obj["day_of_year"] - 1, milliseconds=obj["milliseconds"]
)

return base + timedelta

Expand All @@ -124,11 +124,3 @@ def _decode(self, obj, context, path):

def _encode(self, obj, context, path):
raise NotImplementedError


class ComplexAdapter(Adapter):
def _decode(self, obj, context, path):
return obj["real"] + 1j * obj["imaginary"]

def _encode(self, obj, context, path):
return {"real": obj.real, "imaginary": obj.imag}
4 changes: 2 additions & 2 deletions ceos_alos2/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

try:
ExceptionGroup
except NameError:
from exceptiongroup import ExceptionGroup
except NameError: # pragma: no cover
from exceptiongroup import ExceptionGroup # pragma: no cover

entry_re = re.compile(r'(?P<section>[A-Za-z]{3})_(?P<keyword>.*?)="(?P<value>.*?)"')

Expand Down
139 changes: 138 additions & 1 deletion ceos_alos2/tests/test_datatypes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import datetime

import numpy as np
import pytest
from construct import Bytes, Int8ub, Int32ub, Int64ub, Struct

from ceos_alos2 import datatypes

Expand All @@ -9,7 +12,7 @@
(
pytest.param(b"15", 2, 15, id="2bytes-no_padding"),
pytest.param(b"3989", 4, 3989, id="4bytes-no_padding"),
pytest.param(b"16 ", 4, 16, id="4bytes-padding"),
pytest.param(b" 16", 4, 16, id="4bytes-padding"),
pytest.param(b" ", 4, -1, id="4bytes-all_padding"),
),
)
Expand All @@ -19,6 +22,9 @@ def test_ascii_integer(data, n_bytes, expected):
actual = parser.parse(data)
assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "n_bytes", "expected"],
Expand All @@ -36,6 +42,9 @@ def test_ascii_float(data, n_bytes, expected):
actual = parser.parse(data)
np.testing.assert_equal(actual, expected)

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "n_bytes", "expected"],
Expand All @@ -52,6 +61,9 @@ def test_ascii_complex(data, n_bytes, expected):
actual = parser.parse(data)
np.testing.assert_equal(actual, expected)

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "n_bytes", "expected"],
Expand All @@ -65,3 +77,128 @@ def test_padded_string(data, n_bytes, expected):

actual = parser.parse(data)
assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "factor", "expected"],
(
pytest.param(b"\x32", 1e-2, 0.5, id="negative_factor"),
pytest.param(b"\x32", 1e3, 50000, id="positive_factor"),
),
)
def test_factor(data, factor, expected):
base = Int8ub
parser = datatypes.Factor(base, factor=factor)

actual = parser.parse(data)

assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "expected"],
(
pytest.param(
b"\x00\x00\x07\xc6\x00\x00\x01\x0e\x03\x19\xf2f",
datetime.datetime(1990, 9, 27, 14, 27, 12, 102000),
),
pytest.param(
b"\x00\x00\x08\x0b\x00\x00\x00\x01\x00\x00\x00\x00",
datetime.datetime(2059, 1, 1),
),
),
)
def test_datetime_ydms(data, expected):
base = Struct(
"year" / Int32ub,
"day_of_year" / Int32ub,
"milliseconds" / Int32ub,
)

parser = datatypes.DatetimeYdms(base)

actual = parser.parse(data)

assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "expected"],
(
pytest.param(
b"\x00\x00\x00\x00\x00\x00\x00\x00",
datetime.datetime(2019, 1, 1),
id="offset_zero",
),
pytest.param(
b"\x00\x00\x00\tx\x0f\xb1@",
datetime.datetime(2019, 1, 1, 11, 17, 49),
id="full_seconds",
),
),
)
def test_datetime_ydus(data, expected):
reference_date = datetime.datetime(2019, 1, 1, 21, 37, 52, 107000)

parser = datatypes.DatetimeYdus(Int64ub, reference_date)
actual = parser.parse(data)

assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["metadata"],
(
pytest.param({"units": "m"}),
pytest.param({"scale": 10, "units": "us"}),
),
)
def test_metadata(metadata):
data = b"\x32"
base = Int8ub

expected = (50, metadata)

parser = datatypes.Metadata(base, **metadata)
actual = parser.parse(data)

assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)


@pytest.mark.parametrize(
["data", "expected"],
(
pytest.param(b"\x01", b"\x01", id="no_padding"),
pytest.param(b"\x00\x01", b"\x01", id="left_padding-1"),
pytest.param(b"\x00\x00\x01", b"\x01", id="left_padding-2"),
pytest.param(b"\x01\x00", b"\x01", id="right_padding-1"),
pytest.param(b"\x01\x00\x00", b"\x01", id="right_padding-2"),
pytest.param(b"\x00\x01\x00", b"\x01", id="both_padding-1"),
pytest.param(b"\x00\x00\x01\x00\x00", b"\x01", id="both_padding-1"),
pytest.param(b"\x01\x00\x01", b"\x01\x00\x01", id="mid"),
),
)
def test_strip_null_bytes(data, expected):
base = Bytes(len(data))
parser = datatypes.StripNullBytes(base)

actual = parser.parse(data)
assert actual == expected

with pytest.raises(NotImplementedError):
parser.build(expected)
5 changes: 5 additions & 0 deletions ceos_alos2/tests/test_decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
pytest.param(
"ALOS2225333200-a87433",
ValueError("invalid scene id"),
id="invalid_id-invalid_date_chars",
),
pytest.param(
"ALOS2225333200-987433",
ValueError("invalid scene id"),
id="invalid_id-invalid_date",
),
),
Expand Down
39 changes: 39 additions & 0 deletions ceos_alos2/tests/test_dicttoolz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pytest

from ceos_alos2 import dicttoolz


@pytest.mark.parametrize(
["data", "expected"],
(
pytest.param(
{1: 0, 2: 1, 3: 2},
({3: 2}, {1: 0, 2: 1}),
),
pytest.param(
{1: 0, 3: 0, 2: 1},
({}, {1: 0, 3: 0, 2: 1}),
),
),
)
def test_itemsplit(data, expected):
predicate = lambda item: item[0] % 2 == 1 and item[1] != 0

actual = dicttoolz.itemsplit(predicate, data)
assert actual == expected


def test_valsplit():
data = {0: 1, 1: 0, 2: 2}
actual = dicttoolz.valsplit(lambda v: v != 0, data)
expected = ({0: 1, 2: 2}, {1: 0})

assert actual == expected


def test_keysplit():
data = {0: 1, 1: 0, 2: 2}
actual = dicttoolz.keysplit(lambda k: k % 2 == 0, data)
expected = ({0: 1, 2: 2}, {1: 0})

assert actual == expected
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ known_first_party = "ceos_alos2"

[tool.black]
line-length = 100

[tool.coverage.run]
omit = [
"*/ceos_alos2/tests/*",
]
source = ["ceos_alos2"]
branch = true

[tool.coverage.report]
show_missing = true
exclude_lines = ["pragma: no cover"]

0 comments on commit 56be716

Please sign in to comment.