Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
braingram committed Oct 21, 2024
1 parent 034824c commit 2f270f9
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 1 deletion.
Empty file.
148 changes: 148 additions & 0 deletions src/stdatamodels/jwst/_kwtool/_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import json

import pytest

from stdatamodels.jwst._kwtool import dmd, kwd
from stdatamodels.jwst.datamodels import ImageModel, JwstDataModel


@pytest.fixture(scope="module")
def fake_kwd_path(tmp_path_factory):
kwd_path = tmp_path_factory.mktemp("kwd")

# make 1 top file and 3 ref file
_top = {
"type": "object",
"title": "root",
"properties": {
"meta": {
"title" : "FGS Keywords Schema Metadata",
"type" : "object",
"properties": {
"program": {
"title": "Programmatic information",
"type": "object",
"properties": {
"$ref": "core.program.schema.json"
},
},
"observation": {
"title" : "Observation identifiers",
"type" : "object",
"properties" : {
"allOf" : [
{
"$ref" : "core.observation.schema.json",
},
{
"$ref" : "science.observation.schema.json",
},
],
}
}
}
}
}
}
_core_program = {
"title" : {
"fits_keyword" : "TITLE",
"title" : "Proposal title",
"description" : "na", # actual is longer
"type" : "string",
"units" : "",
"example" : "Cryo2 SIC NIRISS",
"default_value" : "UNKNOWN",
"source" : "Proposal and Planning System (PPS)",
"sw_source" : "PPS:dms_program_view.title",
"calculation" : "",
"destination" : ["ScienceCommon.title", "GuideStar.title"],
"sql_dtype" : "nvarchar(200)",
"si" : "Multiple",
"mode" : "All",
"level" : "1b",
"fits_hdu" : "PRIMARY",
"section" : "Programmatic information",
"misc" : "",
"comment_line" : "/ Program information"
},
}
_core_observation = {
"obs_id": {
"fits_keyword": "OBS_IDD", # modified to trigger compare error
# modified title to trigger compare error
"title" : "Programmatic observation identifier modified",
"description": "na", # actual is longer
"type" : "integer", # modified to trigger compare error
"units" : "",
"example" : "V80600004001P0000000002101",
"default_value" : "",
"special_processing" : "VALUE_REQUIRED",
"source" : "Science Image Header",
"sw_source" : "",
"calculation" : "",
"destination" : ["ScienceCommon.obs_id","GuideStar.obs_id"],
"sql_dtype" : "nvarchar(26)",
"si" : "Multiple",
"mode" : "All",
"level" : "1a",
"fits_hdu" : "SCI", # modified to trigger compare error
"section" : "Observation identifiers",
"misc" : ""
}
}
_science_observation = {
"engineering_quality" : {
"fits_keyword" : "ENG_QUAL",
"title" : "Engineering DB quality indicator",
"description" : "na", # actual is longer
"type" : "string",
"enum" : ["OK", "SUSPECT", "NOT_IN_DATAMODEL"], # modified to trigger compare error
"units" : "",
"example" : "OK",
"default_value" : "OK",
"source" : "Science Data Processing (SDP)",
"sw_source" : "",
"calculation" : "",
"destination" : ["ScienceCommon.eng_qual"],
"sql_dtype" : "nvarchar(15)",
"si" : "ALL",
"mode" : "All",
"level" : "1b",
"fits_hdu" : "PRIMARY",
"section" : "Observation identifiers",
"misc" : "",
"comment_line" : "/ Visit information"
},
}
for fn, schema in [
("top.fgs.schema.json", _top),
("core.program.schema.json", _core_program),
("core.observation.schema.json", _core_observation),
("science.observation.schema.json", _science_observation)
]:
schema_path = kwd_path / fn
with open(schema_path, "w") as f:
json.dump(schema, f)
return kwd_path


@pytest.fixture(scope="module")
def fake_kwd(fake_kwd_path):
return kwd.load(fake_kwd_path)


@pytest.fixture(scope="module")
def limit_dmd_models():
# we can't use the monkeypatch fixture as it's function scoped
with pytest.MonkeyPatch.context() as mp:
mp.setattr(dmd, "_get_subclasses", lambda k: [ImageModel])
yield


@pytest.fixture(scope="module")
def fake_dmd(limit_dmd_models):
"""
A fake datamodel dictionary that includes only 1 datamodel: ImageModel
"""
return dmd.load()
23 changes: 23 additions & 0 deletions src/stdatamodels/jwst/_kwtool/_tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import sys

from stdatamodels.jwst._kwtool import cli


def test_cli(monkeypatch, tmp_path_factory, fake_kwd_path, limit_dmd_models):
# move to a new, empty directory
temp_cwd = tmp_path_factory.mktemp("cwd")
os.chdir(temp_cwd)

output_dir = tmp_path_factory.mktemp("output")
output_path = output_dir / "my_report.html"

monkeypatch.setattr(sys, "argv", [sys.argv[0], str(fake_kwd_path), "-o", str(output_path)])

cli._from_cmdline()

# make sure we didn't write out any local files
assert not os.listdir(temp_cwd)

# and that our output file exists
assert output_path.exists()
135 changes: 135 additions & 0 deletions src/stdatamodels/jwst/_kwtool/_tests/test_compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import pytest

from stdatamodels.jwst._kwtool import compare


def test_filter_standard():
keywords = {
("PRIMARY", "PCOUNT"): 1,
("PRIMARY", "OBS_ID"): 2,
}
result = compare._filter_non_standard(keywords)
assert ("PRIMARY", "OBS_ID") in result
assert ("PRIMARY", "PCOUNT") not in result


def test_filter_non_pattern():
keywords = {
("PRIMARY", "P_BAND"): 1,
("PRIMARY", "PBAND"): 2,
("PRIMARY", "BAND"): 3,
}
result = compare._filter_non_pattern(keywords)
assert ("PRIMARY", "PBAND") in result
assert ("PRIMARY", "BAND") in result
assert ("PRIMARY", "P_BAND") not in result


def test_compare_path_no_destination():
"""
Ignore path differences for keywords that have
no destination.
"""
k = [{
"keyword": {
# no destination
"title": "foo",
},
"path": "a.b.c".split("."),
}]
d = [{
"path": "d.e.f".split("."),
}]
assert compare._compare_path(k, d) is None


def test_compare_path():
# make sure k has a desintation so the difference isn't ignored
k = [{"path": "a.b.c".split("."), "keyword": {"destination": "somewhere"}}]
d = [{"path": "d.e.f".split(".")}]
assert compare._compare_path(k, d) == {"kwd": {"a.b.c"}, "dmd": {"d.e.f"}}


@pytest.mark.parametrize(
"kv, dv, expected", [
("a", "a", None),
("a", "z", {"kwd": {"a"}, "dmd": {"z"}}),
("z", "a", {"kwd": {"z"}, "dmd": {"a"}}),
("a", None, {"kwd": {"a"}, "dmd": {compare._MISSING_VALUE}}),
(None, "a", {"kwd": {compare._MISSING_VALUE}, "dmd": {"a"}}),
(None, None, None),
]
)
def test_compare_keyword_subitem(kv, dv, expected):
# if kv/dv is None, don't fill title
if kv is None:
k = [{"keyword": {}}]
else:
k = [{"keyword": {"title": kv}}]
if dv is None:
d = [{"keyword": {}}]
else:
d = [{"keyword": {"title": dv}}]
assert compare._compare_keyword_subitem(k, d, "title") == expected


@pytest.mark.parametrize(
"ktype, dtype, expected", [
("string", "string", None),
("integer", "string", {"kwd": {"integer"}, "dmd": {"string"}}),
("string", "integer", {"kwd": {"string"}, "dmd": {"integer"}}),
("float", "number", None),
]
)
def test_compare_type(ktype, dtype, expected):
k = [{"keyword": {"type": ktype}}]
d = [{"keyword": {"type": dtype}}]
assert compare._compare_type(k, d) == expected


def test_compare_enum():
# TODO
pass


@pytest.fixture(scope="module")
def compare_result(fake_kwd_path):
return compare.compare_keywords(fake_kwd_path)


def test_obs_id(compare_result):
"""
We constructed a fake keyword dictionary with OBS_IDD
and the datamodels have OBS_ID. Check this was found.
"""
in_k, in_d, in_both, def_diff, k_keys, d_keys = compare_result
assert ("SCI", "OBS_IDD") in in_k
assert ("PRIMARY", "OBS_ID") in in_d


def test_title(compare_result):
"""
TITLE should be in both and agree
"""
in_k, in_d, in_both, def_diff, k_keys, d_keys = compare_result
key = ("PRIMARY", "TITLE")
assert key in in_both
assert key not in def_diff


def test_eng_qual(compare_result):
"""
ENG_QUAL is in both but the enum differs.
There are other differences (due to our test setup) but we'll
ignore these.
"""
in_k, in_d, in_both, def_diff, k_keys, d_keys = compare_result
key = ("PRIMARY", "ENG_QUAL")
assert key in in_both
assert key in def_diff
assert "enum" in def_diff[key]
assert def_diff[key]["enum"] == {
"dmd": {"OK", "SUSPECT"},
"kwd": {"OK", "SUSPECT", "NOT_IN_DATAMODEL"},
}
41 changes: 41 additions & 0 deletions src/stdatamodels/jwst/_kwtool/_tests/test_dmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
monkeypatch _get_subclasses to only return 1 datamodel (to make things easier)
separately test _get_subclasses
"""
import pytest

from stdatamodels.jwst.datamodels import JwstDataModel, FgsImgPhotomModel
from stdatamodels.jwst._kwtool import dmd


def test_get_subclasses():
# more of a smoke test
assert FgsImgPhotomModel in dmd._get_subclasses(JwstDataModel)


@pytest.fixture(params=[("PRIMARY", "TITLE"), ("PRIMARY", "OBS_ID"), ("PRIMARY", "ENG_QUAL")])
def keyword_list(request, fake_dmd):
"""
There are many more keywords than the ones used here. These
were chosen to match the ones added in the fake keyword dictionary.
"""
return fake_dmd[request.param]


@pytest.fixture()
def keyword(keyword_list):
"""
The real datamodel dictionary will contain multiple entires for 1 "keyword"
(a FITS_HDU, FITS_KEYWORD pair). The test dictionary does not, this
fixture helps to test just the first found entry.
"""
return keyword_list[0]

Check warning on line 32 in src/stdatamodels/jwst/_kwtool/_tests/test_dmd.py

View check run for this annotation

Codecov / codecov/patch

src/stdatamodels/jwst/_kwtool/_tests/test_dmd.py#L32

Added line #L32 was not covered by tests


def test_found(keyword_list):
"""
This uses the keyword_list fixture to make sure that the test dictionary
only contains one entry per keyword pair (an assumption made by the
keyword fixture above).
"""
assert len(keyword_list) == 1
37 changes: 37 additions & 0 deletions src/stdatamodels/jwst/_kwtool/_tests/test_kwd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Tests here use the fake_kwd fixture in conftest
"""
import pytest


@pytest.fixture(params=[("PRIMARY", "TITLE"), ("SCI", "OBS_IDD"), ("PRIMARY", "ENG_QUAL")])
def keyword_list(request, fake_kwd):
return fake_kwd[request.param]


@pytest.fixture()
def keyword(keyword_list):
"""
The real keyword dictionary will contain multiple entires for 1 "keyword"
(a FITS_HDU, FITS_KEYWORD pair). The test dictionary does not, this
fixture helps to test just the first found entry.
"""
return keyword_list[0]


def test_fits_keyword_count(fake_kwd):
assert len(fake_kwd) == 3


def test_found(keyword_list):
"""
This uses the keyword_list fixture to make sure that the test dictionary
only contains one entry per keyword pair (an assumption made by the
keyword fixture above).
"""
assert len(keyword_list) == 1


@pytest.mark.parametrize("missing_key", ["allOf", "properties"])
def test_path_sanitized(missing_key, keyword):
assert missing_key not in keyword["path"]
2 changes: 1 addition & 1 deletion src/stdatamodels/jwst/_kwtool/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _compare_path(k, d):
# that have an archive destination TODO document this
for i in k:
if i['keyword'].get('destination'):
# since there is a destination, report the differenc
# since there is a destination, report the difference
return paths
return None

Expand Down

0 comments on commit 2f270f9

Please sign in to comment.