-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
385 additions
and
1 deletion.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
|
||
|
||
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 | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters