125 lines
3.6 KiB
Python
125 lines
3.6 KiB
Python
|
|
# Copyright (C) CVAT.ai Corporation
|
||
|
|
#
|
||
|
|
# SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
import pickle
|
||
|
|
from copy import deepcopy
|
||
|
|
|
||
|
|
from cvat_sdk import models
|
||
|
|
from deepdiff import DeepDiff
|
||
|
|
|
||
|
|
|
||
|
|
def test_models_do_not_change_input_values():
|
||
|
|
# Nested containers may be modified during the model input data parsing.
|
||
|
|
# This can lead to subtle memory errors, which are very hard to find.
|
||
|
|
original_input_data = {
|
||
|
|
"name": "test",
|
||
|
|
"labels": [
|
||
|
|
{
|
||
|
|
"name": "cat",
|
||
|
|
"attributes": [
|
||
|
|
{
|
||
|
|
"default_value": "yy",
|
||
|
|
"input_type": "text",
|
||
|
|
"mutable": False,
|
||
|
|
"name": "x",
|
||
|
|
"values": ["yy"],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"default_value": "1",
|
||
|
|
"input_type": "radio",
|
||
|
|
"mutable": False,
|
||
|
|
"name": "y",
|
||
|
|
"values": ["1", "2"],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
}
|
||
|
|
],
|
||
|
|
}
|
||
|
|
|
||
|
|
input_data = deepcopy(original_input_data)
|
||
|
|
|
||
|
|
models.TaskWriteRequest(**input_data)
|
||
|
|
|
||
|
|
assert DeepDiff(original_input_data, input_data) == {}
|
||
|
|
|
||
|
|
|
||
|
|
def test_models_do_not_store_input_collections():
|
||
|
|
# Avoid depending on input data for collection fields after the model is initialized.
|
||
|
|
# This can lead to subtle memory errors and unexpected behavior
|
||
|
|
# if the original input data is modified.
|
||
|
|
input_data = {
|
||
|
|
"name": "test",
|
||
|
|
"labels": [
|
||
|
|
{
|
||
|
|
"name": "cat1",
|
||
|
|
"attributes": [
|
||
|
|
{
|
||
|
|
"default_value": "yy",
|
||
|
|
"input_type": "text",
|
||
|
|
"mutable": False,
|
||
|
|
"name": "x",
|
||
|
|
"values": ["yy"],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"default_value": "1",
|
||
|
|
"input_type": "radio",
|
||
|
|
"mutable": False,
|
||
|
|
"name": "y",
|
||
|
|
"values": ["1", "2"],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{"name": "cat2", "attributes": []},
|
||
|
|
],
|
||
|
|
}
|
||
|
|
|
||
|
|
model = models.TaskWriteRequest(**input_data)
|
||
|
|
model_data1 = model.to_dict()
|
||
|
|
|
||
|
|
# Modify input value containers
|
||
|
|
input_data["labels"][0]["attributes"].clear()
|
||
|
|
input_data["labels"][1]["attributes"].append(
|
||
|
|
{
|
||
|
|
"default_value": "",
|
||
|
|
"input_type": "text",
|
||
|
|
"mutable": True,
|
||
|
|
"name": "z",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
input_data["labels"].append({"name": "dog"})
|
||
|
|
|
||
|
|
model_data2 = model.to_dict()
|
||
|
|
|
||
|
|
assert DeepDiff(model_data1, model_data2) == {}
|
||
|
|
|
||
|
|
|
||
|
|
def test_models_do_not_return_internal_collections():
|
||
|
|
# Avoid returning internal data for mutable collection fields.
|
||
|
|
# This can lead to subtle memory errors and unexpected behavior
|
||
|
|
# if the returned data is modified.
|
||
|
|
input_data = {
|
||
|
|
"name": "test",
|
||
|
|
"labels": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
model = models.TaskWriteRequest(**input_data)
|
||
|
|
model_data1 = model.to_dict()
|
||
|
|
model_data1_original = deepcopy(model_data1)
|
||
|
|
|
||
|
|
# Modify an output value container
|
||
|
|
model_data1["labels"].append({"name": "dog"})
|
||
|
|
|
||
|
|
model_data2 = model.to_dict()
|
||
|
|
|
||
|
|
assert DeepDiff(model_data1_original, model_data2) == {}
|
||
|
|
|
||
|
|
|
||
|
|
def test_models_are_pickleable():
|
||
|
|
model = models.PatchedLabelRequest(id=5, name="person")
|
||
|
|
pickled_model = pickle.dumps(model)
|
||
|
|
unpickled_model = pickle.loads(pickled_model)
|
||
|
|
|
||
|
|
assert unpickled_model.id == model.id
|
||
|
|
assert unpickled_model.name == model.name
|