Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dependencies = [
"pyvisa>=1.11.0, <1.17.0",
"ruamel.yaml>=0.16.0,!=0.16.6",
"tabulate>=0.9.0",
"typing_extensions>=4.6.0",
"typing_extensions>=4.13.0",
"tqdm>=4.59.0",
"uncertainties>=3.2.0",
"versioningit>=2.2.1",
Expand Down
83 changes: 83 additions & 0 deletions tests/dataset/test_export_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from __future__ import annotations

from qcodes.dataset.export_config import (
DataExportType,
get_data_export_name_elements,
get_data_export_prefix,
get_data_export_type,
set_data_export_prefix,
set_data_export_type,
)


def test_data_export_type_enum_members() -> None:
assert DataExportType.NETCDF.value == "nc"
assert DataExportType.CSV.value == "csv"
assert len(DataExportType) == 2


def test_get_data_export_type_with_string_netcdf() -> None:
result = get_data_export_type("NETCDF")
assert result is DataExportType.NETCDF


def test_get_data_export_type_with_string_csv() -> None:
result = get_data_export_type("CSV")
assert result is DataExportType.CSV


def test_get_data_export_type_case_insensitive() -> None:
assert get_data_export_type("netcdf") is DataExportType.NETCDF
assert get_data_export_type("csv") is DataExportType.CSV
assert get_data_export_type("Csv") is DataExportType.CSV


def test_get_data_export_type_with_enum_input() -> None:
result = get_data_export_type(DataExportType.NETCDF)
assert result is DataExportType.NETCDF

result = get_data_export_type(DataExportType.CSV)
assert result is DataExportType.CSV


def test_get_data_export_type_with_none_returns_none() -> None:
# When config export_type is also None/empty, should return None
set_data_export_type(None) # type: ignore[arg-type]
result = get_data_export_type(None)
assert result is None


def test_get_data_export_type_with_invalid_string_returns_none() -> None:
result = get_data_export_type("nonexistent_format")
assert result is None


def test_set_and_get_data_export_prefix_roundtrip() -> None:
set_data_export_prefix("my_prefix_")
assert get_data_export_prefix() == "my_prefix_"

set_data_export_prefix("")
assert get_data_export_prefix() == ""


def test_get_data_export_name_elements_returns_list() -> None:
result = get_data_export_name_elements()
assert isinstance(result, list)


def test_set_data_export_type_valid() -> None:
set_data_export_type("netcdf")
result = get_data_export_type()
assert result is DataExportType.NETCDF

set_data_export_type("csv")
result = get_data_export_type()
assert result is DataExportType.CSV


def test_set_data_export_type_invalid_does_not_change_config() -> None:
set_data_export_type("netcdf")
set_data_export_type("invalid_type")
# Config should still have the previous valid value
result = get_data_export_type()
assert result is DataExportType.NETCDF
135 changes: 135 additions & 0 deletions tests/dataset/test_json_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from __future__ import annotations

import copy
import json
from pathlib import Path

import numpy as np

from qcodes.dataset.json_exporter import (
export_data_as_json_heatmap,
export_data_as_json_linear,
json_template_heatmap,
json_template_linear,
)


def test_json_template_linear_structure() -> None:
assert json_template_linear["type"] == "linear"
assert "x" in json_template_linear
assert "y" in json_template_linear
assert isinstance(json_template_linear["x"], dict)
assert isinstance(json_template_linear["y"], dict)
assert "data" in json_template_linear["x"]
assert "data" in json_template_linear["y"]
assert json_template_linear["x"]["is_setpoint"] is True
assert json_template_linear["y"]["is_setpoint"] is False


def test_json_template_heatmap_structure() -> None:
assert json_template_heatmap["type"] == "heatmap"
assert "x" in json_template_heatmap
assert "y" in json_template_heatmap
assert "z" in json_template_heatmap
assert isinstance(json_template_heatmap["x"], dict)
assert isinstance(json_template_heatmap["y"], dict)
assert isinstance(json_template_heatmap["z"], dict)
assert json_template_heatmap["x"]["is_setpoint"] is True
assert json_template_heatmap["y"]["is_setpoint"] is True
assert json_template_heatmap["z"]["is_setpoint"] is False


def test_export_linear_writes_correct_json(tmp_path: Path) -> None:
location = str(tmp_path / "linear.json")
state: dict = {"json": copy.deepcopy(json_template_linear)}
data = [[1.0, 10.0], [2.0, 20.0], [3.0, 30.0]]

export_data_as_json_linear(data, len(data), state, location)

with open(location) as f:
result = json.load(f)

assert result["type"] == "linear"
assert result["x"]["data"] == [1.0, 2.0, 3.0]
assert result["y"]["data"] == [10.0, 20.0, 30.0]


def test_export_linear_accumulates_data(tmp_path: Path) -> None:
location = str(tmp_path / "linear.json")
state: dict = {"json": copy.deepcopy(json_template_linear)}

export_data_as_json_linear([[1.0, 10.0]], 1, state, location)
export_data_as_json_linear([[2.0, 20.0]], 2, state, location)

with open(location) as f:
result = json.load(f)

assert result["x"]["data"] == [1.0, 2.0]
assert result["y"]["data"] == [10.0, 20.0]


def test_export_linear_does_nothing_for_empty_data(tmp_path: Path) -> None:
location = str(tmp_path / "linear.json")
state: dict = {"json": copy.deepcopy(json_template_linear)}

export_data_as_json_linear([], 0, state, location)

assert not Path(location).exists()


def test_export_heatmap_writes_correct_json(tmp_path: Path) -> None:
location = str(tmp_path / "heatmap.json")
xlen = 2
ylen = 3
total = xlen * ylen

state: dict = {
"json": copy.deepcopy(json_template_heatmap),
"data": {
"x": np.zeros(total),
"y": np.zeros(total),
"z": np.zeros(total),
"location": 0,
"xlen": xlen,
"ylen": ylen,
},
}

# 2x3 grid: x varies slowly, y varies fast
data = [
[0.0, 0.0, 1.0],
[0.0, 1.0, 2.0],
[0.0, 2.0, 3.0],
[1.0, 0.0, 4.0],
[1.0, 1.0, 5.0],
[1.0, 2.0, 6.0],
]

export_data_as_json_heatmap(data, total, state, location)

with open(location) as f:
result = json.load(f)

assert result["type"] == "heatmap"
assert result["x"]["data"] == [0.0, 1.0]
assert result["y"]["data"] == [0.0, 1.0, 2.0]
assert result["z"]["data"] == [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]


def test_export_heatmap_does_nothing_for_empty_data(tmp_path: Path) -> None:
location = str(tmp_path / "heatmap.json")
state: dict = {
"json": copy.deepcopy(json_template_heatmap),
"data": {
"x": np.zeros(4),
"y": np.zeros(4),
"z": np.zeros(4),
"location": 0,
"xlen": 2,
"ylen": 2,
},
}

export_data_as_json_heatmap([], 0, state, location)

assert not Path(location).exists()
169 changes: 169 additions & 0 deletions tests/dataset/test_rundescribertypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""
Tests for qcodes.dataset.descriptions.versioning.rundescribertypes.

Verifies the TypedDict classes, inheritance relationships, type aliases,
and the RunDescriberDicts union.
"""

from __future__ import annotations

import typing

from typing_extensions import get_annotations, get_original_bases

from qcodes.dataset.descriptions.versioning.rundescribertypes import (
InterDependencies_Dict,
InterDependenciesDict,
RunDescriberDicts,
RunDescriberV0Dict,
RunDescriberV1Dict,
RunDescriberV2Dict,
RunDescriberV3Dict,
Shapes,
)

# --------------- Shapes type alias ---------------


def test_shapes_type_alias() -> None:
sample: Shapes = {"param": (1, 2, 3)}
assert sample["param"] == (1, 2, 3)


# --------------- InterDependenciesDict ---------------


def test_interdependencies_dict_instantiation() -> None:
d: InterDependenciesDict = {"paramspecs": ()}
assert d["paramspecs"] == ()


# --------------- InterDependencies_Dict ---------------


def test_interdependencies_underscore_dict_instantiation() -> None:
d: InterDependencies_Dict = {
"parameters": {},
"dependencies": {},
"inferences": {},
"standalones": [],
}
assert d["parameters"] == {}
assert d["standalones"] == []


# --------------- RunDescriberV0Dict ---------------


def test_v0_dict_instantiation() -> None:
d: RunDescriberV0Dict = {
"version": 0,
"interdependencies": {"paramspecs": ()},
}
assert d["version"] == 0


# --------------- RunDescriberV1Dict ---------------


def test_v1_dict_instantiation() -> None:
d: RunDescriberV1Dict = {
"version": 1,
"interdependencies": {
"parameters": {},
"dependencies": {},
"inferences": {},
"standalones": [],
},
}
assert d["version"] == 1


# --------------- RunDescriberV2Dict inherits from V0 ---------------


def test_v2_dict_inherits_from_v0() -> None:
# typing_extensions TypedDict flattens __bases__ to (dict,) at runtime;
# verify structural inheritance via __orig_bases__ and annotations.
assert RunDescriberV0Dict in get_original_bases(RunDescriberV2Dict)
# V2 should contain all V0 keys plus its own
v0_keys = set(get_annotations(RunDescriberV0Dict))
v2_keys = set(get_annotations(RunDescriberV2Dict))
assert v0_keys.issubset(v2_keys)


def test_v2_dict_instantiation() -> None:
d: RunDescriberV2Dict = {
"version": 2,
"interdependencies": {"paramspecs": ()},
"interdependencies_": {
"parameters": {},
"dependencies": {},
"inferences": {},
"standalones": [],
},
}
assert d["version"] == 2
assert "interdependencies_" in d


# --------------- RunDescriberV3Dict inherits from V2 ---------------


def test_v3_dict_inherits_from_v2() -> None:
assert RunDescriberV2Dict in get_original_bases(RunDescriberV3Dict)
v2_keys = set(get_annotations(RunDescriberV2Dict))
v3_keys = set(get_annotations(RunDescriberV3Dict))
assert v2_keys.issubset(v3_keys)


def test_v3_dict_inherits_from_v0_transitively() -> None:
# V3 inherits from V2 which inherits from V0 — all V0 keys present
v0_keys = set(get_annotations(RunDescriberV0Dict))
v3_keys = set(get_annotations(RunDescriberV3Dict))
assert v0_keys.issubset(v3_keys)


def test_v3_dict_instantiation() -> None:
d: RunDescriberV3Dict = {
"version": 3,
"interdependencies": {"paramspecs": ()},
"interdependencies_": {
"parameters": {},
"dependencies": {},
"inferences": {},
"standalones": [],
},
"shapes": {"x": (10,)},
}
assert d["version"] == 3
assert d["shapes"] == {"x": (10,)}


def test_v3_dict_shapes_none() -> None:
d: RunDescriberV3Dict = {
"version": 3,
"interdependencies": {"paramspecs": ()},
"interdependencies_": {
"parameters": {},
"dependencies": {},
"inferences": {},
"standalones": [],
},
"shapes": None,
}
assert d["shapes"] is None


# --------------- RunDescriberDicts union ---------------


def test_rundescriber_dicts_includes_all_versions() -> None:
args = typing.get_args(RunDescriberDicts)
expected = {
RunDescriberV0Dict,
RunDescriberV1Dict,
RunDescriberV2Dict,
RunDescriberV3Dict,
}
assert set(args) == expected
Loading
Loading