Files
EnglewoodLAB/Assets/Sample/Data/DataObjectTests.cs

247 lines
11 KiB
C#

#nullable enable
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using UnityEngine;
using UVC.Data.Core;
namespace UVC.Sample.Data
{
/// <summary>
/// DataObject 기능 샘플 코드입니다.
/// DataSampleRunner Inspector에서 실행하거나 Play 버튼으로 확인합니다.
/// </summary>
public class DataObjectTests
{
private DataObject _dataObject = new DataObject();
/// <summary>
/// 모든 샘플을 순차 실행합니다.
/// </summary>
public void RunAll()
{
Debug.Log("===== DataObject 샘플 시작 =====");
SetUp();
Run(nameof(UpdateDifferent_WhenCalled_UpdatesAndTracksChanges), UpdateDifferent_WhenCalled_UpdatesAndTracksChanges);
SetUp();
Run(nameof(Indexer_WhenPropertyIsSet_TracksChange), Indexer_WhenPropertyIsSet_TracksChange);
SetUp();
Run(nameof(Indexer_WhenNewPropertyIsAdded_TracksAsChange), Indexer_WhenNewPropertyIsAdded_TracksAsChange);
SetUp();
Run(nameof(GetUpdatedObject_WhenCalledAfterChanges_ReturnsOnlyUpdatedProperties), GetUpdatedObject_WhenCalledAfterChanges_ReturnsOnlyUpdatedProperties);
SetUp();
Run(nameof(MarkAllAsUpdated_WhenCalled_MarksAllPropertiesAsChanged), MarkAllAsUpdated_WhenCalled_MarksAllPropertiesAsChanged);
SetUp();
Run(nameof(Remove_WhenCalled_RemovesPropertyAndChangeTracking), Remove_WhenCalled_RemovesPropertyAndChangeTracking);
SetUp();
Run(nameof(GetTypeMethods_ReturnCorrectTypes), GetTypeMethods_ReturnCorrectTypes);
SetUp();
Run(nameof(Constructor_FromJObject_CreatesCorrectObject), Constructor_FromJObject_CreatesCorrectObject);
SetUp();
Run(nameof(IdProperty_ReturnsCorrectId), IdProperty_ReturnsCorrectId);
SetUp();
Run(nameof(KeyOrder_IsPreservedByInsertion), KeyOrder_IsPreservedByInsertion);
SetUp();
Run(nameof(DataObjectPool_GetAndReturn_WorksCorrectly), DataObjectPool_GetAndReturn_WorksCorrectly);
Debug.Log("===== DataObject 샘플 완료 =====");
}
private void SetUp()
{
_dataObject = new DataObject
{
{ "Id", 1 },
{ "name", "Test" },
{ "value", 100.5f },
{ "isActive", true }
};
_dataObject.ClearChangedProperties();
}
private void Run(string name, Action action)
{
try
{
action();
Debug.Log($"[OK] {name}");
}
catch (Exception ex)
{
Debug.LogError($"[FAIL] {name}\n{ex.Message}");
}
}
private static void Check(bool condition, string message)
{
if (!condition) throw new Exception($"검증 실패: {message}");
}
/// <summary>
/// UpdateDifferent: 변경된 프로퍼티만 추적해서 업데이트합니다.
/// </summary>
public void UpdateDifferent_WhenCalled_UpdatesAndTracksChanges()
{
var otherDataObject = new DataObject
{
{ "Id", 1 },
{ "name", "Updated Test" },
{ "newValue", "new" }
};
_dataObject.UpdateDifferent(otherDataObject, true);
Check((string)_dataObject["name"] == "Updated Test", "name이 'Updated Test'여야 합니다.");
Check((string)_dataObject["newValue"] == "new", "newValue가 'new'여야 합니다.");
Check(_dataObject.UpdatedCount == 2, "UpdatedCount는 2여야 합니다.");
Check(_dataObject.ChangedProperies.Contains("name"), "ChangedProperies에 'name'이 포함되어야 합니다.");
Check(_dataObject.ChangedProperies.Contains("newValue"), "ChangedProperies에 'newValue'가 포함되어야 합니다.");
}
/// <summary>
/// Indexer: 기존 프로퍼티 변경 시 변경 추적됩니다.
/// </summary>
public void Indexer_WhenPropertyIsSet_TracksChange()
{
_dataObject["name"] = "New Name";
Check(_dataObject.UpdatedCount == 1, "UpdatedCount는 1이어야 합니다.");
Check(_dataObject.ChangedProperies.Contains("name"), "ChangedProperies에 'name'이 포함되어야 합니다.");
}
/// <summary>
/// Indexer: 새 프로퍼티 추가 시 변경으로 추적됩니다.
/// </summary>
public void Indexer_WhenNewPropertyIsAdded_TracksAsChange()
{
_dataObject["newKey"] = "newValue";
Check(_dataObject.UpdatedCount == 1, "새로 추가된 속성은 변경으로 간주되어야 합니다.");
Check(_dataObject.ChangedProperies.Contains("newKey"), "ChangedProperies에 'newKey'가 포함되어야 합니다.");
}
/// <summary>
/// GetUpdatedObject: 변경된 프로퍼티를 반환합니다.
/// IdKey 미설정 시 첫 번째 키("Id")가 자동으로 포함되므로 최소 Count는 변경 수 + 1입니다.
/// </summary>
public void GetUpdatedObject_WhenCalledAfterChanges_ReturnsOnlyUpdatedProperties()
{
_dataObject["name"] = "Updated Name";
_dataObject["value"] = 200.0f;
var updatedObject = _dataObject.GetUpdatedObject() as DataObject;
Check(updatedObject != null, "updatedObject가 null이 아니어야 합니다.");
// IdKey 미설정 시 첫 번째 키("Id")가 자동 포함 → 변경 2개 + Id 1개 = 3개
Check(updatedObject!.Count == 3, "변경된 프로퍼티 2개 + 자동 포함 Id 1개 = 3개여야 합니다.");
Check((string)updatedObject["name"] == "Updated Name", "name이 'Updated Name'이어야 합니다.");
Check(updatedObject.ContainsKey("value"), "value 키가 포함되어야 합니다.");
// IdKey 없으면 첫 번째 키(Id)가 자동 포함됩니다.
Check(updatedObject.ContainsKey("Id"), "IdKey 미설정 시 첫 번째 키 'Id'가 자동 포함되어야 합니다.");
}
/// <summary>
/// MarkAllAsUpdated: 모든 프로퍼티를 변경 상태로 표시합니다.
/// </summary>
public void MarkAllAsUpdated_WhenCalled_MarksAllPropertiesAsChanged()
{
_dataObject.MarkAllAsUpdated();
Check(_dataObject.UpdatedCount == _dataObject.Count, "UpdatedCount는 전체 프로퍼티 수와 같아야 합니다.");
foreach (var key in _dataObject.Keys)
Check(_dataObject.ChangedProperies.Contains(key), $"ChangedProperies에 '{key}'가 포함되어야 합니다.");
}
/// <summary>
/// Remove: 프로퍼티 제거 시 변경 추적도 함께 제거됩니다.
/// </summary>
public void Remove_WhenCalled_RemovesPropertyAndChangeTracking()
{
_dataObject["name"] = "A new name to be removed";
Check(_dataObject.ChangedProperies.Contains("name"), "사전 조건: 'name'이 변경 목록에 있어야 합니다.");
bool result = _dataObject.Remove("name");
Check(result, "Remove는 true를 반환해야 합니다.");
Check(!_dataObject.ContainsKey("name"), "'name' 키가 제거되어야 합니다.");
Check(!_dataObject.ChangedProperies.Contains("name"), "변경 추적에서도 'name'이 제거되어야 합니다.");
}
/// <summary>
/// 타입별 Get 메서드가 올바른 타입을 반환합니다.
/// </summary>
public void GetTypeMethods_ReturnCorrectTypes()
{
Check(_dataObject.GetInt("Id") == 1, "GetInt('Id')는 1이어야 합니다.");
Check(_dataObject.GetString("name") == "Test", "GetString('name')은 'Test'여야 합니다.");
Check(_dataObject.GetFloat("value") == 100.5f, "GetFloat('value')는 100.5f여야 합니다.");
Check(_dataObject.GetBool("isActive") == true, "GetBool('isActive')는 true여야 합니다.");
}
/// <summary>
/// JObject 생성자로 DataObject를 만들 수 있습니다.
/// </summary>
public void Constructor_FromJObject_CreatesCorrectObject()
{
var jObject = new JObject
{
{ "Id", 10 },
{ "user", "jobject_user" }
};
var dataObject = new DataObject(jObject);
Check(dataObject.GetInt("Id") == 10, "GetInt('Id')는 10이어야 합니다.");
Check(dataObject.GetString("user") == "jobject_user", "GetString('user')는 'jobject_user'여야 합니다.");
Check(dataObject.UpdatedCount == 0, "생성자 호출 시 변경 추적을 하지 않아야 합니다.");
}
/// <summary>
/// IdKey 설정에 따라 Id 프로퍼티가 올바르게 동작합니다.
/// </summary>
public void IdProperty_ReturnsCorrectId()
{
_dataObject.IdKey = "Id";
Check(_dataObject.Id == "1", "IdKey='Id' 일 때 Id는 '1'이어야 합니다.");
var noIdKeyObject = new DataObject { { "name", "Test" }, { "value", 100 } };
Check(noIdKeyObject.Id == "Test", "IdKey 미설정 시 첫 번째 값이 Id여야 합니다.");
}
/// <summary>
/// 삽입 순서대로 Key 순서가 유지됩니다.
/// </summary>
public void KeyOrder_IsPreservedByInsertion()
{
var orderedDataObject = new DataObject();
orderedDataObject.Add("first", 1);
orderedDataObject.Add("second", 2);
orderedDataObject.Add("third", 3);
var keys = orderedDataObject.Keys.ToList();
Check(keys[0] == "first", "첫 번째 키는 'first'여야 합니다.");
Check(keys[1] == "second", "두 번째 키는 'second'여야 합니다.");
Check(keys[2] == "third", "세 번째 키는 'third'여야 합니다.");
}
/// <summary>
/// DataObjectPool: 풀에서 가져오고 반환하는 기능을 확인합니다.
/// </summary>
public void DataObjectPool_GetAndReturn_WorksCorrectly()
{
var obj = DataObjectPool.Get();
obj["test"] = "value";
Check(obj != null, "풀에서 가져온 객체는 null이 아니어야 합니다.");
Check(obj.Count == 1, "객체에 1개의 항목이 있어야 합니다.");
DataObjectPool.Return(obj);
var retrievedObj = DataObjectPool.Get();
Check(retrievedObj.Count == 0, "풀에서 다시 가져온 객체는 비어있어야 합니다.");
}
}
}