Data 패키지 개선

This commit is contained in:
logonkhi
2025-06-10 20:16:35 +09:00
parent 649a359ab4
commit 7ef6825368
22 changed files with 1416 additions and 248 deletions

View File

@@ -41,6 +41,10 @@ namespace UVC.Tests.Data
RunTest(nameof(Map_ComplexNestedStructure_MapsCorrectly), Map_ComplexNestedStructure_MapsCorrectly);
RunTest(nameof(Map_MixedArrayTypes_HandlesCorrectly), Map_MixedArrayTypes_HandlesCorrectly);
RunTest(nameof(Map_WithNamesForReplace_ShouldRenameProperties), Map_WithNamesForReplace_ShouldRenameProperties);
RunTest(nameof(Map_DeepClone_CopiesAllProperties), Map_DeepClone_CopiesAllProperties);
RunTest(nameof(Map_ToJObject_ConvertsCorrectly), Map_ToJObject_ConvertsCorrectly);
RunTest(nameof(Map_ParallelArrayProcessing_WorksCorrectly), Map_ParallelArrayProcessing_WorksCorrectly);
RunTest(nameof(Map_RecursionDepthLimit_HandlesCorrectly), Map_RecursionDepthLimit_HandlesCorrectly);
Debug.Log("===== DataMapper 테스트 완료 =====");
}
@@ -80,7 +84,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Name"));
@@ -104,7 +108,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Age"));
@@ -128,7 +132,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Height"));
@@ -151,7 +155,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("IsActive"));
@@ -175,7 +179,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("BirthDate"));
@@ -200,7 +204,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Status"));
@@ -225,7 +229,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Status"));
@@ -247,7 +251,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Status"));
@@ -269,7 +273,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsFalse(result.ContainsKey("Email"));
@@ -291,7 +295,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("BirthDate"));
@@ -327,7 +331,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.AreEqual(6, result.Count);
@@ -374,7 +378,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("User"));
@@ -399,7 +403,7 @@ namespace UVC.Tests.Data
// Arrange
var mask = new DataMask
{
["Contacts"] = new JArray
["Contacts"] = new List<DataMask>
{
new DataMask
{
@@ -419,7 +423,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Contacts"));
@@ -448,7 +452,7 @@ namespace UVC.Tests.Data
// Arrange
var mask = new DataMask
{
["Tags"] = new JArray()
["Tags"] = new List<DataMask>()
};
var source = JObject.Parse(@"{
@@ -458,7 +462,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("Tags"));
@@ -472,7 +476,7 @@ namespace UVC.Tests.Data
var item = tags[i];
Assert.IsNotNull(item);
// DataObject는 "value" 키에 실제 값을 저장
Assert.AreEqual(((JArray)source["Tags"])[i].ToString(), item.GetString("value"));
Assert.AreEqual((source["Tags"])[i].ToString(), item.GetString("value"));
}
}
@@ -489,12 +493,12 @@ namespace UVC.Tests.Data
{
["Name"] = "회사명",
["Founded"] = JToken.FromObject(DateTime.Now),
["Departments"] = new JArray
["Departments"] = new List<DataMask>
{
new DataMask
{
["Name"] = "부서명",
["Employees"] = new JArray
["Employees"] = new List<DataMask>
{
new DataMask
{
@@ -535,7 +539,7 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
var company = result.GetDataObject("Company");
@@ -573,25 +577,15 @@ namespace UVC.Tests.Data
public void Map_MixedArrayTypes_HandlesCorrectly()
{
// Arrange
var mask = new DataMask
{
["MixedArray"] = new JArray
{
"문자열",
123,
true,
new DataMask { ["Key"] = "Value" },
new JArray{1, 1}
}
};
var mask = new DataMask(@"{
""MixedArray"": [
{ ""Key"": ""Value"", ""Key1"": 1, ""Key2"": true},
]
}");
var source = JObject.Parse(@"{
""MixedArray"": [
""테스트"",
456,
false,
{ ""Key"": ""NewValue"", ""Extra"": ""ExtraValue"" },
[1, 2, 3],
{ ""Key"": ""NewValue"", ""Key1"": 456, ""Key2"": false, ""Extra"": ""ExtraValue"" },
{ ""items"": [1, 2, 3] }
]
}");
@@ -599,40 +593,30 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
Assert.IsTrue(result.ContainsKey("MixedArray"));
var mixedArray = result.GetDataArray("MixedArray");
Assert.IsNotNull(mixedArray);
Assert.AreEqual(5, mixedArray.Count);
Assert.AreEqual(2, mixedArray.Count);
// 문자열 항목 검증
Assert.AreEqual("테스트", mixedArray[0].GetString("value"));
Assert.AreEqual("NewValue", mixedArray[0].GetString("Key"));
// 숫자 항목 검증
Assert.AreEqual(456, mixedArray[1].GetInt("value"));
Assert.AreEqual(456, mixedArray[0].GetInt("Key1"));
// 불리언 항목 검증
Assert.AreEqual(false, mixedArray[2].GetBool("value"));
Assert.AreEqual(false, mixedArray[0].GetBool("Key2"));
// 객체 항목 검증
var objInArray = mixedArray[3];
var objInArray = mixedArray[0];
Assert.IsNotNull(objInArray);
Assert.AreEqual("NewValue", objInArray.GetString("Key"));
// 중첩 배열 항목 검증
var nestedItem = mixedArray[4];
Assert.IsNotNull(nestedItem);
// DataMapper는 중첩 배열을 "items" 키를 가진 DataObject로 포장합니다
var nestedArray = nestedItem.GetDataArray("items");
Assert.IsNotNull(nestedArray);
Assert.AreEqual(2, nestedArray.Count);
Assert.AreEqual(1, nestedArray[0].GetInt("value"));
Assert.AreEqual(2, nestedArray[1].GetInt("value"));
}
/// <summary>
/// NamesForReplace 속성이 제대로 구현되었을 때 예상되는 동작을 테스트합니다.
/// NamesForReplace 속성이 제대로 구현되었는지 테스트합니다.
/// </summary>
[Test]
public void Map_WithNamesForReplace_ShouldRenameProperties()
@@ -650,16 +634,183 @@ namespace UVC.Tests.Data
var mapper = new DataMapper(mask);
// Act
var result = mapper.Mapping(source);
var result = mapper.Map(source);
// Assert
// NamesForReplace가 제대로 구현되면 이 테스트는 통과해야 함
Assert.IsTrue(result.ContainsKey("NewName"));
Assert.AreEqual("김철수", result.GetString("NewName"));
Assert.IsFalse(result.ContainsKey("OldName"));
Assert.IsTrue(result.ContainsKey("Age"));
Assert.AreEqual(30, result.GetInt("Age"));
}
/// <summary>
/// DataMask의 DeepClone 메서드가 모든 속성을 올바르게 복사하는지 테스트합니다.
/// </summary>
[Test]
public void Map_DeepClone_CopiesAllProperties()
{
// Arrange
var originalMask = new DataMask();
originalMask["Name"] = "홍길동";
originalMask["Age"] = 25;
originalMask.ObjectIdKey = "Name";
originalMask.ObjectName = "Person";
originalMask.NamesForReplace = new Dictionary<string, string> {
{ "full_name", "Name" },
{ "user_age", "Age" }
};
// Act
var clonedMask = originalMask.DeepClone();
// 원본 마스크 수정
originalMask["Name"] = "변경된 이름";
originalMask.ObjectName = "ModifiedPerson";
originalMask.NamesForReplace["full_name"] = "FullName";
// Assert - 클론은 변경되지 않아야 함
Assert.AreEqual("홍길동", clonedMask["Name"]);
Assert.AreEqual(25, clonedMask["Age"]);
Assert.AreEqual("Name", clonedMask.ObjectIdKey);
Assert.AreEqual("Person", clonedMask.ObjectName);
Assert.AreEqual("Name", clonedMask.NamesForReplace["full_name"]);
Assert.AreEqual("Age", clonedMask.NamesForReplace["user_age"]);
// 원본 마스크는 변경되어야 함
Assert.AreEqual("변경된 이름", originalMask["Name"]);
Assert.AreEqual("ModifiedPerson", originalMask.ObjectName);
Assert.AreEqual("FullName", originalMask.NamesForReplace["full_name"]);
}
/// <summary>
/// DataMask의 ToJObject 메서드가 올바르게 동작하는지 테스트합니다.
/// </summary>
[Test]
public void Map_ToJObject_ConvertsCorrectly()
{
// Arrange
var mask = new DataMask();
mask["Name"] = "홍길동";
mask["Age"] = 25;
mask["IsActive"] = true;
mask["Height"] = 175.5;
// Act
var jObject = mask.ToJObject();
// Assert
Assert.IsNotNull(jObject);
Assert.AreEqual(4, jObject.Count);
Assert.AreEqual("홍길동", jObject["Name"].ToString());
Assert.AreEqual(25, jObject["Age"].ToObject<int>());
Assert.AreEqual(true, jObject["IsActive"].ToObject<bool>());
Assert.AreEqual(175.5, jObject["Height"].ToObject<double>());
}
/// <summary>
/// DataMapper의 병렬 처리 기능이 대용량 배열에서 올바르게 동작하는지 테스트합니다.
/// </summary>
[Test]
public void Map_ParallelArrayProcessing_WorksCorrectly()
{
// Arrange
var mask = new DataMask();
mask["Items"] = new List<DataMask> { new DataMask { ["Id"] = 0, ["Name"] = "" } };
// 2000개 아이템을 가진 소스 배열 생성
var itemsArray = new JArray();
for (int i = 1; i <= 2000; i++)
{
itemsArray.Add(new JObject
{
["Id"] = i,
["Name"] = $"Item {i}"
});
}
var source = new JObject { ["Items"] = itemsArray };
var mapper = new DataMapper(mask);
// Act
var result = mapper.Map(source);
// Assert
var items = result.GetDataArray("Items");
Assert.IsNotNull(items);
Assert.AreEqual(2000, items.Count);
// 결과 검증 - 몇 개 아이템만 샘플링
Assert.AreEqual(1, items[0].GetInt("Id"));
Assert.AreEqual("Item 1", items[0].GetString("Name"));
Assert.AreEqual(1000, items[999].GetInt("Id"));
Assert.AreEqual("Item 1000", items[999].GetString("Name"));
Assert.AreEqual(2000, items[1999].GetInt("Id"));
Assert.AreEqual("Item 2000", items[1999].GetString("Name"));
}
/// <summary>
/// 재귀 깊이 제한이 올바르게 동작하는지 테스트합니다.
/// </summary>
[Test]
public void Map_RecursionDepthLimit_HandlesCorrectly()
{
// Arrange - 매우 깊게 중첩된 JSON 객체 생성
var mask = CreateDeepNestedMask(20);
var source = CreateDeepNestedJObject(20);
var mapper = new DataMapper(mask);
string maskJsonString = mask.ToJsonString();
// Act - 예외 없이 매핑이 수행되어야 함
var result = mapper.Map(source);
// Assert - 최대 중첩 깊이(현재 10)까지 매핑된 구조 확인
var level1 = result.GetDataObject("Level20");
Assert.IsNotNull(level1);
var level5 = level1.GetDataObject("Level15")
.GetDataObject("Level14")
.GetDataObject("Level13")
.GetDataObject("Level12");
Assert.IsNotNull(level5);
// 깊이 제한 이후 원본 객체를 그대로 반환하므로
// 원래 깊이보다 더 깊게 들어갈 수 있어야 함
var level10 = level5.GetDataObject("Level10")
.GetDataObject("Level9")
.GetDataObject("Level8")
.GetDataObject("Level7")
.GetDataObject("Level6");
Assert.IsNotNull(level10);
}
/// <summary>
/// 테스트용 깊게 중첩된 DataMask 생성
/// </summary>
private DataMask CreateDeepNestedMask(int depth)
{
if (depth <= 0) return new DataMask { ["Value"] = "마스크 값" };
var mask = new DataMask();
mask[$"Level{depth}"] = CreateDeepNestedMask(depth - 1);
return mask;
}
/// <summary>
/// 테스트용 깊게 중첩된 JObject 생성
/// </summary>
private JObject CreateDeepNestedJObject(int depth)
{
if (depth <= 0) return new JObject { ["Value"] = "소스 값" };
var obj = new JObject();
obj[$"Level{depth}"] = CreateDeepNestedJObject(depth - 1);
return obj;
}
}
/// <summary>

View File

@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using UnityEngine;
using UVC.Data;
using UVC.Log;
namespace UVC.Tests.Data
{
@@ -42,14 +43,14 @@ namespace UVC.Tests.Data
{
Setup();
Debug.Log("===== MQTTPipeLine 테스트 시작 =====");
await RunTestAsync(nameof(ExecutePipeLine_AllTopics_RegistersAndHandlesMessages), ExecutePipeLine_AllTopics_RegistersAndHandlesMessages);
await RunTestAsync(nameof(RemoveTopic_ShouldStopReceivingMessages), RemoveTopic_ShouldStopReceivingMessages);
await RunTestAsync(nameof(UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData), UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData);
//await RunTestAsync(nameof(ExecutePipeLine_AllTopics_RegistersAndHandlesMessages), ExecutePipeLine_AllTopics_RegistersAndHandlesMessages);
//await RunTestAsync(nameof(RemoveTopic_ShouldStopReceivingMessages), RemoveTopic_ShouldStopReceivingMessages);
//await RunTestAsync(nameof(UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData), UpdatedDataOnly_ShouldOnlyCallHandlerForUpdatedData);
await RunTestAsync(nameof(UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData), UpdatedDataOnly_WithMockMQTTService_ShouldOnlyReceiveUpdatedData);
RunTest(nameof(OnTopicMessage_ValidJsonObject_CallsHandler), OnTopicMessage_ValidJsonObject_CallsHandler);
RunTest(nameof(OnTopicMessage_JsonArray_CallsHandler), OnTopicMessage_JsonArray_CallsHandler);
RunTest(nameof(OnTopicMessage_EmptyMessage_DoesNotCallHandler), OnTopicMessage_EmptyMessage_DoesNotCallHandler);
RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler);
//RunTest(nameof(OnTopicMessage_ValidJsonObject_CallsHandler), OnTopicMessage_ValidJsonObject_CallsHandler);
//RunTest(nameof(OnTopicMessage_JsonArray_CallsHandler), OnTopicMessage_JsonArray_CallsHandler);
//RunTest(nameof(OnTopicMessage_EmptyMessage_DoesNotCallHandler), OnTopicMessage_EmptyMessage_DoesNotCallHandler);
//RunTest(nameof(OnTopicMessage_InvalidJson_DoesNotCallHandler), OnTopicMessage_InvalidJson_DoesNotCallHandler);
Debug.Log("===== MQTTPipeLine 테스트 완료 =====");
}
@@ -99,6 +100,7 @@ namespace UVC.Tests.Data
{
case "AGV":
dataMask.ObjectIdKey = "VHL_NAME";
dataMask.ObjectName = "AGV";
dataMask["VHL_NAME"] = "HFF09CNA8013";
dataMask["AGV_IDX"] = 12;
dataMask["B_INSTALL"] = "Y";
@@ -126,6 +128,7 @@ namespace UVC.Tests.Data
break;
case "CARRIER":
dataMask.ObjectIdKey = "MAIN_CARR_ID";
dataMask.ObjectName = "Carrier";
dataMask["MAIN_CARR_ID"] = "2F02365";
dataMask["SUB_CARR_ID"] = "2F02365,2F70671,2F28723";
dataMask["CARR_SEQ"] = "3";
@@ -144,6 +147,7 @@ namespace UVC.Tests.Data
break;
case "STOCKER_STACK":
dataMask.ObjectIdKey = "STOCKER_NAME";
dataMask.ObjectName = "StokerStack";
dataMask["STOCKER_NAME"] = "HFF09AGN0300";
dataMask["CAPACITY"] = "89.57";
dataMask["MAXIMUM_CAPACITY"] = "834";
@@ -160,9 +164,11 @@ namespace UVC.Tests.Data
dataMask["KOR_EQP_NAME"] = "상온Aging #03";
dataMask["ENG_EQP_NAME"] = "상온Aging #03";
dataMask["TIMESTAMP"] = DateTime.Now;
dataMask["STEP"] = new JArray
dataMask["STEP"] = new List<DataMask>()
{
new DataObject {
new DataMask {
ObjectIdKey = "STOCKER_NAME",
ObjectName = "StokerStep",
["STOCKER_NAME"] = "HFF09AGN0300",
["STEP_ID"] = "8106",
["RACK_STEP_COUNT"] = "88",
@@ -173,9 +179,9 @@ namespace UVC.Tests.Data
break;
case "ALL":
// ALL 토픽은 ObjectIdKey 없음
dataMask["AGV"] = new JArray()
dataMask["AGV"] = new List<DataMask>()
{
new DataObject
new DataMask
{
["VHL_NAME"] = "HFF09CNA8053",
["AGV_IDX"] = 52,
@@ -202,9 +208,9 @@ namespace UVC.Tests.Data
["FACTOR"] = 69.3,
}
};
dataMask["CARRIER"] = new JArray()
dataMask["CARRIER"] = new List<DataMask>()
{
new DataObject
new DataMask
{
["MAIN_CARR_ID"] = "2F02365",
["SUB_CARR_ID"] = "2F02365,2F70671,2F28723",
@@ -223,9 +229,9 @@ namespace UVC.Tests.Data
["CURRENTLOCATION"] = "HFF09CNV0300",
}
};
dataMask["CARRIER"] = new JArray()
dataMask["CARRIER"] = new List<DataMask>()
{
new DataObject
new DataMask
{
["STOCKER_NAME"] = "HFF09AGN0300",
["CAPACITY"] = "89.57",
@@ -243,9 +249,9 @@ namespace UVC.Tests.Data
["KOR_EQP_NAME"] = "상온Aging #03",
["ENG_EQP_NAME"] = "상온Aging #03",
["TIMESTAMP"] = DateTime.Now,
["STEP"] = new JArray
["STEP"] = new List<DataMask>
{
new DataObject {
new DataMask {
["STOCKER_NAME"] = "HFF09AGN0300",
["STEP_ID"] = "8106",
["RACK_STEP_COUNT"] = "88",
@@ -276,10 +282,10 @@ namespace UVC.Tests.Data
pipeLine.Add(pipelineInfo);
}
Debug.Log("파이프라인 설정 완료.");
// Act - 파이프라인 실행
pipeLine.Execute();
Debug.Log("파이프라인 Execute.");
// Assert - 일정 시간 기다린 후 각 핸들러가 호출되었는지 확인
await UniTask.Delay(1500);
@@ -495,7 +501,7 @@ namespace UVC.Tests.Data
Assert.IsTrue(firstUpdatedCount > 0, "첫 번째 메시지에 업데이트된 데이터가 없습니다.");
// 다음 데이터 세트가 도착하기를 기다림
await UniTask.Delay(1500);
await UniTask.Delay(3000);
// 두 번째 메시지가 도착했는지 확인
int finalCallCount = handler.CallCount;
@@ -532,7 +538,7 @@ namespace UVC.Tests.Data
public void HandleData(IDataObject? dataObject)
{
CallCount++;
ULog.Debug($"UpdatedDataTrackingHandler 호출됨. CallCount: {CallCount}");
if (dataObject != null)
{
// 업데이트 개수 기록
@@ -553,7 +559,6 @@ namespace UVC.Tests.Data
{
HasUpdatedExistingItems = true;
}
// 새로운 항목 추가
ReceivedAgvItems.Add(vhlName);
}

File diff suppressed because one or more lines are too long