247 lines
11 KiB
C#
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, "풀에서 다시 가져온 객체는 비어있어야 합니다.");
|
|
}
|
|
}
|
|
}
|