Patching.cpp 184 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "FileIOBaseTestTypes.h"
  9. #include <AzCore/Serialization/DataPatch.h>
  10. #include <AzCore/Serialization/DynamicSerializableField.h>
  11. #include <AzCore/Serialization/Utils.h>
  12. #include <AzCore/UnitTest/TestTypes.h>
  13. #include <AzCore/std/any.h>
  14. namespace AZ
  15. {
  16. // Added definition of type info and rtti for the DataPatchTypeUpgrade class
  17. // to this Unit Test file to allow rtti functions to be accessible from the SerializeContext::TypeChange
  18. // call
  19. AZ_TYPE_INFO_TEMPLATE_WITH_NAME_IMPL(SerializeContext::DataPatchTypeUpgrade, "DataPatchTypeUpgrade", "{E5A2F519-261C-4B81-925F-3730D363AB9C}", AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_CLASS);
  20. AZ_RTTI_NO_TYPE_INFO_IMPL((SerializeContext::DataPatchTypeUpgrade, AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_CLASS), DataPatchUpgrade);
  21. }
  22. using namespace AZ;
  23. namespace UnitTest
  24. {
  25. /**
  26. * Tests generating and applying patching to serialized structures.
  27. * \note There a lots special... \TODO add notes depending on the final solution
  28. */
  29. namespace Patching
  30. {
  31. // Object that we will store in container and patch in the complex case
  32. class ContainedObjectPersistentId
  33. {
  34. public:
  35. AZ_TYPE_INFO(ContainedObjectPersistentId, "{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}")
  36. ContainedObjectPersistentId()
  37. : m_data(0)
  38. , m_persistentId(0)
  39. {}
  40. u64 GetPersistentId() const { return m_persistentId; }
  41. void SetPersistentId(u64 pesistentId) { m_persistentId = pesistentId; }
  42. int m_data;
  43. u64 m_persistentId; ///< Returns the persistent object ID
  44. static u64 GetPersistentIdWrapper(const void* instance)
  45. {
  46. return reinterpret_cast<const ContainedObjectPersistentId*>(instance)->GetPersistentId();
  47. }
  48. static void Reflect(AZ::SerializeContext& sc)
  49. {
  50. sc.Class<ContainedObjectPersistentId>()->
  51. PersistentId(&ContainedObjectPersistentId::GetPersistentIdWrapper)->
  52. Field("m_data", &ContainedObjectPersistentId::m_data)->
  53. Field("m_persistentId", &ContainedObjectPersistentId::m_persistentId);
  54. }
  55. };
  56. class ContainedObjectDerivedPersistentId
  57. : public ContainedObjectPersistentId
  58. {
  59. public:
  60. AZ_TYPE_INFO(ContainedObjectDerivedPersistentId, "{1c3ba36a-ceee-4118-89e7-807930bf2bec}");
  61. static void Reflect(AZ::SerializeContext& sc)
  62. {
  63. sc.Class<ContainedObjectDerivedPersistentId, ContainedObjectPersistentId>();
  64. }
  65. };
  66. class ContainedObjectNoPersistentId
  67. {
  68. public:
  69. AZ_CLASS_ALLOCATOR(ContainedObjectNoPersistentId, SystemAllocator);
  70. AZ_TYPE_INFO(ContainedObjectNoPersistentId, "{A9980498-6E7A-42C0-BF9F-DFA48142DDAB}");
  71. ContainedObjectNoPersistentId()
  72. : m_data(0)
  73. {}
  74. ContainedObjectNoPersistentId(int data)
  75. : m_data(data)
  76. {}
  77. int m_data;
  78. static void Reflect(AZ::SerializeContext& sc)
  79. {
  80. sc.Class<ContainedObjectNoPersistentId>()->
  81. Field("m_data", &ContainedObjectNoPersistentId::m_data);
  82. }
  83. };
  84. class SimpleClassContainingVectorOfInts
  85. {
  86. public:
  87. AZ_TYPE_INFO(SimpleClassContainingVectorOfInts, "{82FE64FA-23DB-40B5-BD1B-9DC145CB86EA}");
  88. AZ_CLASS_ALLOCATOR(SimpleClassContainingVectorOfInts, AZ::SystemAllocator);
  89. virtual ~SimpleClassContainingVectorOfInts() = default;
  90. static void Reflect(AZ::SerializeContext& sc)
  91. {
  92. sc.Class<SimpleClassContainingVectorOfInts>()
  93. ->Field("id", &SimpleClassContainingVectorOfInts::m_id);
  94. }
  95. AZStd::vector<int> m_id;
  96. };
  97. class CommonPatch
  98. {
  99. public:
  100. AZ_RTTI(CommonPatch, "{81FE64FA-23DB-40B5-BD1B-9DC145CB86EA}");
  101. AZ_CLASS_ALLOCATOR(CommonPatch, AZ::SystemAllocator);
  102. virtual ~CommonPatch() = default;
  103. static void Reflect(AZ::SerializeContext& sc)
  104. {
  105. sc.Class<CommonPatch>()
  106. ->SerializeWithNoData();
  107. }
  108. };
  109. class ObjectToPatch
  110. : public CommonPatch
  111. {
  112. public:
  113. AZ_RTTI(ObjectToPatch, "{47E5CF10-3FA1-4064-BE7A-70E3143B4025}", CommonPatch);
  114. AZ_CLASS_ALLOCATOR(ObjectToPatch, AZ::SystemAllocator);
  115. ObjectToPatch() = default;
  116. ObjectToPatch(const ObjectToPatch&) = delete;
  117. int m_intValue = 0;
  118. AZStd::vector<ContainedObjectPersistentId> m_objectArray;
  119. AZStd::vector<ContainedObjectDerivedPersistentId> m_derivedObjectArray;
  120. AZStd::unordered_map<u32, AZStd::unique_ptr<ContainedObjectNoPersistentId>> m_objectMap;
  121. AZStd::vector<ContainedObjectNoPersistentId> m_objectArrayNoPersistentId;
  122. AZ::DynamicSerializableField m_dynamicField;
  123. ~ObjectToPatch() override
  124. {
  125. m_dynamicField.DestroyData();
  126. }
  127. static void Reflect(AZ::SerializeContext& sc)
  128. {
  129. sc.Class<ObjectToPatch, CommonPatch>()->
  130. Field("m_dynamicField", &ObjectToPatch::m_dynamicField)->
  131. Field("m_intValue", &ObjectToPatch::m_intValue)->
  132. Field("m_objectArray", &ObjectToPatch::m_objectArray)->
  133. Field("m_derivedObjectArray", &ObjectToPatch::m_derivedObjectArray)->
  134. Field("m_objectMap", &ObjectToPatch::m_objectMap)->
  135. Field("m_objectArrayNoPersistentId", &ObjectToPatch::m_objectArrayNoPersistentId);
  136. }
  137. };
  138. class DifferentObjectToPatch
  139. : public CommonPatch
  140. {
  141. public:
  142. AZ_RTTI(DifferentObjectToPatch, "{2E107ABB-E77A-4188-AC32-4CA8EB3C5BD1}", CommonPatch);
  143. AZ_CLASS_ALLOCATOR(DifferentObjectToPatch, AZ::SystemAllocator);
  144. float m_data;
  145. static void Reflect(AZ::SerializeContext& sc)
  146. {
  147. sc.Class<DifferentObjectToPatch, CommonPatch>()->
  148. Field("m_data", &DifferentObjectToPatch::m_data);
  149. }
  150. };
  151. class ObjectsWithGenerics
  152. {
  153. public:
  154. AZ_CLASS_ALLOCATOR(ObjectsWithGenerics, SystemAllocator);
  155. AZ_TYPE_INFO(ObjectsWithGenerics, "{DE1EE15F-3458-40AE-A206-C6C957E2432B}");
  156. static void Reflect(AZ::SerializeContext& sc)
  157. {
  158. sc.Class<ObjectsWithGenerics>()->
  159. Field("m_string", &ObjectsWithGenerics::m_string);
  160. }
  161. AZStd::string m_string;
  162. };
  163. class ObjectWithPointer
  164. {
  165. public:
  166. AZ_CLASS_ALLOCATOR(ObjectWithPointer, SystemAllocator);
  167. AZ_TYPE_INFO(ObjectWithPointer, "{D1FD3240-A7C5-4EA3-8E55-CD18193162B8}");
  168. static void Reflect(AZ::SerializeContext& sc)
  169. {
  170. sc.Class<ObjectWithPointer>()
  171. ->Field("m_int", &ObjectWithPointer::m_int)
  172. ->Field("m_pointerInt", &ObjectWithPointer::m_pointerInt)
  173. ;
  174. }
  175. AZ::s32 m_int;
  176. AZ::s32* m_pointerInt = nullptr;
  177. };
  178. class ObjectWithMultiPointers
  179. {
  180. public:
  181. AZ_CLASS_ALLOCATOR(ObjectWithMultiPointers, SystemAllocator);
  182. AZ_TYPE_INFO(ObjectWithMultiPointers, "{EBA25BFA-CFA0-4397-929C-A765BA72DE28}");
  183. static void Reflect(AZ::SerializeContext& sc)
  184. {
  185. sc.Class<ObjectWithMultiPointers>()
  186. ->Field("m_int", &ObjectWithMultiPointers::m_int)
  187. ->Field("m_pointerInt", &ObjectWithMultiPointers::m_pointerInt)
  188. ->Field("m_pointerFloat", &ObjectWithMultiPointers::m_pointerFloat)
  189. ;
  190. }
  191. AZ::s32 m_int;
  192. AZ::s32* m_pointerInt = nullptr;
  193. float* m_pointerFloat = nullptr;
  194. };
  195. static AZStd::string IntToString(int)
  196. {
  197. AZ_Assert(false, "Version 0 Type Converter for ObjectWithNumericFieldV1 should never be called");
  198. return {};
  199. };
  200. // If the version 1 to 2 version converter ran
  201. // A sentinel value of 32.0 is always returned which can be represented exactly
  202. // in floating point(power of 2)
  203. static double IntToDouble(int)
  204. {
  205. return 32.0;
  206. };
  207. struct ObjectWithNumericFieldV1
  208. {
  209. AZ_CLASS_ALLOCATOR(ObjectWithNumericFieldV1, SystemAllocator);
  210. AZ_TYPE_INFO(ObjectWithNumericFieldV1, "{556A83B0-77BC-41D1-B3BC-C1CD0A4F5845}");
  211. static void Reflect(AZ::ReflectContext* reflectContext)
  212. {
  213. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  214. {
  215. serializeContext->Class<ObjectWithNumericFieldV1>()
  216. ->Version(1)
  217. ->Field("IntValue", &ObjectWithNumericFieldV1::m_value)
  218. //! Provide a name converter for Version 0 -> 1
  219. //! This should never be called as there is no Version 0 of this class
  220. //! It is here to validate that it is never called
  221. ->NameChange(0, 1, "IntValue", "NameValueThatShouldNeverBeSet")
  222. //! Version 0 -> 1 type converter should never be called
  223. ->TypeChange("IntValue", 0, 1, AZStd::function<AZStd::string(const int&)>(&IntToString))
  224. ;
  225. }
  226. }
  227. int m_value{};
  228. };
  229. struct ObjectWithNumericFieldV2
  230. {
  231. AZ_CLASS_ALLOCATOR(ObjectWithNumericFieldV2, SystemAllocator);
  232. AZ_TYPE_INFO(ObjectWithNumericFieldV2, "{556A83B0-77BC-41D1-B3BC-C1CD0A4F5845}");
  233. static void Reflect(AZ::ReflectContext* reflectContext)
  234. {
  235. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  236. {
  237. serializeContext->Class<ObjectWithNumericFieldV2>()
  238. ->Version(2)
  239. ->Field("DoubleValue", &ObjectWithNumericFieldV2::m_value)
  240. //! Provide a name converter for Version 0 -> 1
  241. //! This should never be called as there is no Version 0 of this class
  242. //! It is here to validate that it is never called
  243. ->NameChange(0, 1, "IntValue", "NameValueThatShouldNeverBeSet")
  244. //! Version 0 -> 1 type converter should never be called
  245. ->TypeChange("IntValue", 0, 1, AZStd::function<AZStd::string(const int&)>(&IntToString))
  246. ->NameChange(1, 2, "IntValue", "DoubleValue")
  247. ->TypeChange("IntValue", 1, 2, AZStd::function<double(const int&)>(&IntToDouble))
  248. ;
  249. }
  250. }
  251. double m_value{};
  252. };
  253. class InnerObjectFieldConverterV1
  254. {
  255. public:
  256. AZ_CLASS_ALLOCATOR(InnerObjectFieldConverterV1, SystemAllocator);
  257. AZ_RTTI(InnerObjectFieldConverterV1, "{28E61B17-F321-4D4E-9F4C-00846C6631DE}");
  258. virtual ~InnerObjectFieldConverterV1() = default;
  259. static void Reflect(AZ::ReflectContext* reflectContext)
  260. {
  261. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  262. {
  263. serializeContext->Class<InnerObjectFieldConverterV1>()
  264. ->Version(1)
  265. ->Field("InnerBaseStringField", &InnerObjectFieldConverterV1::m_stringField)
  266. ->Field("InnerBaseStringVector", &InnerObjectFieldConverterV1::m_stringVector)
  267. ;
  268. }
  269. }
  270. //! AZStd::string uses IDataSerializer for Serialization.
  271. //! This is to test Field Converters for patched element that are on a class that is a descendant element of the patched class
  272. AZStd::string m_stringField;
  273. //! AZStd::string uses IDataSerializer for Serialization.
  274. //! This is to test Field Converters for patched element that are on a class that is a descendant element of the patched class
  275. AZStd::vector<AZStd::string> m_stringVector;
  276. };
  277. template<typename BaseClass>
  278. class InnerObjectFieldConverterDerivedV1Template
  279. : public BaseClass
  280. {
  281. public:
  282. AZ_CLASS_ALLOCATOR(InnerObjectFieldConverterDerivedV1Template, SystemAllocator);
  283. AZ_RTTI(InnerObjectFieldConverterDerivedV1Template, "{C68BE9B8-33F8-4969-B521-B44F5BA1C0DE}", BaseClass);
  284. static void Reflect(AZ::ReflectContext* reflectContext)
  285. {
  286. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  287. {
  288. serializeContext->Class<InnerObjectFieldConverterDerivedV1Template, BaseClass>()
  289. ->Version(1)
  290. ->Field("InnerDerivedNumericField", &InnerObjectFieldConverterDerivedV1Template::m_objectWithNumericField)
  291. ;
  292. }
  293. }
  294. //! ObjectWithNumericFieldV1 uses the normal SerializeContext::ClassBulder for for Serialization.
  295. //! This is to test Field Converters for a patched element serialized in a pointer to the base class
  296. ObjectWithNumericFieldV1 m_objectWithNumericField;
  297. };
  298. using InnerObjectFieldConverterDerivedV1 = InnerObjectFieldConverterDerivedV1Template<InnerObjectFieldConverterV1>;
  299. class ObjectFieldConverterV1
  300. {
  301. public:
  302. AZ_CLASS_ALLOCATOR(ObjectFieldConverterV1, SystemAllocator);
  303. AZ_TYPE_INFO(ObjectFieldConverterV1, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  304. static void Reflect(AZ::ReflectContext* reflectContext)
  305. {
  306. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  307. {
  308. serializeContext->Class<ObjectFieldConverterV1>()
  309. ->Version(1)
  310. ->Field("RootStringField", &ObjectFieldConverterV1::m_rootStringField)
  311. ->Field("RootStringVector", &ObjectFieldConverterV1::m_rootStringVector)
  312. ->Field("RootInnerObjectValue", &ObjectFieldConverterV1::m_rootInnerObject)
  313. ->Field("RootInnerObjectPointer", &ObjectFieldConverterV1::m_baseInnerObjectPolymorphic)
  314. ;
  315. }
  316. }
  317. //! AZStd::string uses IDataSerializer for Serialization.
  318. //! This is to test Field Converters for patched element that are directly on the patched class
  319. AZStd::string m_rootStringField;
  320. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  321. //! This is to test Field Converters for patched element that are directly on the patched class
  322. AZStd::vector<AZStd::string> m_rootStringVector;
  323. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  324. //! This is to test Field Converters for patched element that are directly on the patched class
  325. InnerObjectFieldConverterV1 m_rootInnerObject{};
  326. InnerObjectFieldConverterV1* m_baseInnerObjectPolymorphic{};
  327. };
  328. class ObjectBaseClass
  329. {
  330. public:
  331. AZ_CLASS_ALLOCATOR(ObjectBaseClass, SystemAllocator);
  332. AZ_RTTI(ObjectBaseClass, "{9CFEC143-9C78-4566-A541-46F9CA6FE66E}");
  333. virtual ~ObjectBaseClass() = default;
  334. static void Reflect(AZ::SerializeContext& sc)
  335. {
  336. sc.Class<ObjectBaseClass>();
  337. }
  338. };
  339. class ObjectDerivedClass1 : public ObjectBaseClass
  340. {
  341. public:
  342. AZ_CLASS_ALLOCATOR(ObjectDerivedClass1, SystemAllocator);
  343. AZ_RTTI(ObjectDerivedClass1, "{9D6502E8-999D-46B8-AF37-EAAA0D53385A}", ObjectBaseClass);
  344. static void Reflect(AZ::SerializeContext& sc)
  345. {
  346. sc.Class<ObjectDerivedClass1>();
  347. }
  348. };
  349. class ObjectDerivedClass2 : public ObjectBaseClass
  350. {
  351. public:
  352. AZ_CLASS_ALLOCATOR(ObjectDerivedClass2, SystemAllocator);
  353. AZ_RTTI(ObjectDerivedClass2, "{91D1812E-17A2-4BC3-A9A1-13196BE50803}", ObjectBaseClass);
  354. static void Reflect(AZ::SerializeContext& sc)
  355. {
  356. sc.Class<ObjectDerivedClass2>();
  357. }
  358. };
  359. class ObjectDerivedClass3 : public ObjectBaseClass
  360. {
  361. public:
  362. AZ_CLASS_ALLOCATOR(ObjectDerivedClass3, SystemAllocator);
  363. AZ_RTTI(ObjectDerivedClass3, "{E80E926B-5750-4E8D-80E0-D06057692847}", ObjectBaseClass);
  364. static void Reflect(AZ::SerializeContext& sc)
  365. {
  366. sc.Class<ObjectDerivedClass3>();
  367. }
  368. };
  369. static bool ConvertDerivedClass2ToDerivedClass3(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  370. {
  371. classElement.Convert(context, AZ::AzTypeInfo<ObjectDerivedClass3>::Uuid());
  372. return true;
  373. }
  374. class ObjectWithVectorOfBaseClasses
  375. {
  376. public:
  377. AZ_CLASS_ALLOCATOR(ObjectWithVectorOfBaseClasses, SystemAllocator);
  378. AZ_TYPE_INFO(ObjectWithVectorOfBaseClasses, "{BC9D5346-1BC5-41C4-8CF0-7ACD96F7790F}");
  379. static void Reflect(AZ::SerializeContext& sc)
  380. {
  381. sc.Class<ObjectWithVectorOfBaseClasses>()
  382. ->Field("m_vectorOfBaseClasses", &ObjectWithVectorOfBaseClasses::m_vectorOfBaseClasses);
  383. }
  384. virtual ~ObjectWithVectorOfBaseClasses()
  385. {
  386. for (auto element : m_vectorOfBaseClasses)
  387. {
  388. delete element;
  389. }
  390. m_vectorOfBaseClasses.clear();
  391. }
  392. AZStd::vector<ObjectBaseClass*> m_vectorOfBaseClasses;
  393. };
  394. }
  395. class PatchingTest
  396. : public LeakDetectionFixture
  397. {
  398. protected:
  399. void SetUp() override
  400. {
  401. LeakDetectionFixture::SetUp();
  402. m_serializeContext = AZStd::make_unique<SerializeContext>();
  403. using namespace Patching;
  404. CommonPatch::Reflect(*m_serializeContext);
  405. ContainedObjectPersistentId::Reflect(*m_serializeContext);
  406. ContainedObjectDerivedPersistentId::Reflect(*m_serializeContext);
  407. ContainedObjectNoPersistentId::Reflect(*m_serializeContext);
  408. ObjectToPatch::Reflect(*m_serializeContext);
  409. DifferentObjectToPatch::Reflect(*m_serializeContext);
  410. ObjectsWithGenerics::Reflect(*m_serializeContext);
  411. ObjectWithPointer::Reflect(*m_serializeContext);
  412. ObjectWithMultiPointers::Reflect(*m_serializeContext);
  413. AZ::DataPatch::Reflect(m_serializeContext.get());
  414. const SerializeContext::ClassData* addressTypeSerializerClassData = m_serializeContext.get()->FindClassData(azrtti_typeid<DataPatch::AddressType>());
  415. AZ_Assert(addressTypeSerializerClassData, "AddressType class not reflected, required to run DataPatch Unit Tests");
  416. m_addressTypeSerializer = static_cast<DataPatchInternal::AddressTypeSerializer*>(addressTypeSerializerClassData->m_serializer.get());
  417. AZ_Assert(m_addressTypeSerializer, "AddressTypeSerializer not provided in class AddressType's reflection, required to run DataPatch Unit Tests");
  418. }
  419. void TearDown() override
  420. {
  421. m_serializeContext.reset();
  422. m_addressTypeSerializer = nullptr;
  423. LeakDetectionFixture::TearDown();
  424. }
  425. void LoadPatchFromXML(const AZStd::string_view& xmlSrc, DataPatch& patchDest)
  426. {
  427. AZ::IO::MemoryStream xmlStream(xmlSrc.data(), xmlSrc.size());
  428. Utils::LoadObjectFromStreamInPlace(xmlStream, patchDest, m_serializeContext.get());
  429. }
  430. void LoadPatchFromByteStream(const AZStd::vector<AZ::u8>& byteStreamSrc, DataPatch& patchDest)
  431. {
  432. AZ::IO::MemoryStream streamRead(byteStreamSrc.data(), byteStreamSrc.size());
  433. AZ::Utils::LoadObjectFromStreamInPlace(streamRead, patchDest, m_serializeContext.get());
  434. }
  435. void WritePatchToByteStream(const DataPatch& patchSrc, AZStd::vector<AZ::u8>& byteStreamDest)
  436. {
  437. byteStreamDest.clear();
  438. AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8>> streamWrite(&byteStreamDest);
  439. AZ::Utils::SaveObjectToStream(streamWrite, AZ::DataStream::ST_XML, &patchSrc, m_serializeContext.get());
  440. }
  441. // Template XML that can be formatted for multiple tests
  442. // ObjectToPatch m_intValue override
  443. const char* m_XMLDataPatchV1AddressTypeIntOverrideTemplate = R"(<ObjectStream version="3">
  444. <Class name="DataPatch" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
  445. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  446. <Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}">
  447. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  448. <Class name="AddressType" field="value1" value="%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  449. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  450. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  451. </Class>
  452. </Class>
  453. </Class>
  454. </Class>
  455. </ObjectStream>
  456. )";
  457. // Valid address for above XML for easy formatting of tests expected to pass
  458. const char* m_XMLDataPatchV1AddressTypeIntOverrideValidAddress = R"(int({72039442-EB38-4D42-A1AD-CB68F7E0EEF6})::m_intValue%s0%s)";
  459. // ObjectToPatch m_objectArray overrides. Container is size 5. Can format the first address and each element's data and persistent ids
  460. const char* m_XMLDataPatchV1AddressTypeIntVectorOverrideTemplate = R"(<ObjectStream version="3">
  461. <Class name="DataPatch" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
  462. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  463. <Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}">
  464. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  465. <Class name="AddressType" field="value1" value="%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  466. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  467. <Class name="ContainedObjectPersistentId" field="m_data" type="{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}">
  468. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  469. <Class name="AZ::u64" field="m_persistentId" value="%i" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  470. </Class>
  471. </Class>
  472. </Class>
  473. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  474. <Class name="AddressType" field="value1" value="AZStd::vector({861A12B0-BD91-528E-9CEC-505246EE98DE})::m_objectArray%s0%sContainedObjectPersistentId({D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA})#%i%s0%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  475. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  476. <Class name="ContainedObjectPersistentId" field="m_data" type="{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}">
  477. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  478. <Class name="AZ::u64" field="m_persistentId" value="%i" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  479. </Class>
  480. </Class>
  481. </Class>
  482. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  483. <Class name="AddressType" field="value1" value="AZStd::vector({861A12B0-BD91-528E-9CEC-505246EE98DE})::m_objectArray%s0%sContainedObjectPersistentId({D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA})#%i%s0%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  484. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  485. <Class name="ContainedObjectPersistentId" field="m_data" type="{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}">
  486. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  487. <Class name="AZ::u64" field="m_persistentId" value="%i" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  488. </Class>
  489. </Class>
  490. </Class>
  491. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  492. <Class name="AddressType" field="value1" value="AZStd::vector({861A12B0-BD91-528E-9CEC-505246EE98DE})::m_objectArray%s0%sContainedObjectPersistentId({D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA})#%i%s0%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  493. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  494. <Class name="ContainedObjectPersistentId" field="m_data" type="{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}">
  495. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  496. <Class name="AZ::u64" field="m_persistentId" value="%i" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  497. </Class>
  498. </Class>
  499. </Class>
  500. <Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
  501. <Class name="AddressType" field="value1" value="AZStd::vector({861A12B0-BD91-528E-9CEC-505246EE98DE})::m_objectArray%s0%sContainedObjectPersistentId({D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA})#%i%s0%s" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  502. <Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}">
  503. <Class name="ContainedObjectPersistentId" field="m_data" type="{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}">
  504. <Class name="int" field="m_data" value="%i" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
  505. <Class name="AZ::u64" field="m_persistentId" value="%i" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  506. </Class>
  507. </Class>
  508. </Class>
  509. </Class>
  510. </Class>
  511. </ObjectStream>
  512. )";
  513. // Valid address for above XML for easy formatting of tests expected to pass
  514. const char* m_XMLDataPatchV1AddressTypeIntVectorOverrideValidAddress = R"(AZStd::vector({861A12B0-BD91-528E-9CEC-505246EE98DE})::m_objectArray%s0%sContainedObjectPersistentId({D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA})#%i%s0%s)";
  515. /*
  516. Builds a valid address for m_XMLDataPatchV1AddressTypeIntOverrideTemplate
  517. while allowing formatting of the path and version delimiters
  518. */
  519. AZStd::string GetValidAddressForXMLDataPatchV1AddressTypeIntXML()
  520. {
  521. return AZStd::string::format(m_XMLDataPatchV1AddressTypeIntOverrideValidAddress,
  522. V1AddressTypeElementVersionDelimiter,
  523. V1AddressTypeElementPathDelimiter);
  524. }
  525. /*
  526. Sets the data value for the int type stored in m_XMLDataPatchV1AddressTypeIntOverrideTemplate's xml stream
  527. Can also optionally set the address, otherwise defaults to a valid address if testAddress is nullptr
  528. */
  529. AZStd::string BuildXMLDataPatchV1AddressTypeIntXML(const char* testAddress, int intValue)
  530. {
  531. AZStd::string editableAddress;
  532. if (testAddress)
  533. {
  534. editableAddress = testAddress;
  535. }
  536. else
  537. {
  538. editableAddress = GetValidAddressForXMLDataPatchV1AddressTypeIntXML();
  539. }
  540. return AZStd::string::format(m_XMLDataPatchV1AddressTypeIntOverrideTemplate, editableAddress.c_str(), intValue);
  541. }
  542. /*
  543. Sets the data values and persistentId values for the ContainedObjectPersistentId type stored in m_XMLDataPatchV1AddressTypeIntVectorOverrideTemplate's xml stream
  544. Can also optionally set the address of the first element otherwise defaults to a valid address if testAddress is nullptr
  545. The stream contains 5 elements within the vector
  546. */
  547. AZStd::string BuildXMLDataPatchV1AddressTypeIntVectorXML(const char* testAddress, int dataModifier, int persistentIdModifier)
  548. {
  549. AZStd::string editableAddress;
  550. // Allow customization of our delimiters
  551. const char* versionDelimiter = V1AddressTypeElementVersionDelimiter;
  552. const char* pathDelimiter = V1AddressTypeElementPathDelimiter;
  553. // If a testAddress was supplied then use it.
  554. // Otherwise format with a valid path
  555. if (testAddress)
  556. {
  557. editableAddress = testAddress;
  558. }
  559. else
  560. {
  561. editableAddress = AZStd::string::format(m_XMLDataPatchV1AddressTypeIntVectorOverrideValidAddress,
  562. versionDelimiter,
  563. pathDelimiter,
  564. 4 + persistentIdModifier,
  565. versionDelimiter,
  566. pathDelimiter);
  567. }
  568. // Format our xml.
  569. // It is size 5 so we format the data and persistent IDs to match this size.
  570. return AZStd::string::format(m_XMLDataPatchV1AddressTypeIntVectorOverrideTemplate,
  571. editableAddress.c_str(), 4 + dataModifier, 4 + persistentIdModifier,
  572. versionDelimiter, pathDelimiter, 3 + persistentIdModifier, versionDelimiter, pathDelimiter, 3 + dataModifier, 3 + persistentIdModifier,
  573. versionDelimiter, pathDelimiter, 2 + persistentIdModifier, versionDelimiter, pathDelimiter, 2 + dataModifier, 2 + persistentIdModifier,
  574. versionDelimiter, pathDelimiter, 1 + persistentIdModifier, versionDelimiter, pathDelimiter, 1 + dataModifier, 1 + persistentIdModifier,
  575. versionDelimiter, pathDelimiter, 0 + persistentIdModifier, versionDelimiter, pathDelimiter, 0 + dataModifier, 0 + persistentIdModifier);
  576. }
  577. // Store each AddressTypeElement version's delimiters seperately from the class so our tests don't auto update to a new version if these delimiters change in V2+
  578. static constexpr const char* V1AddressTypeElementPathDelimiter = "/";
  579. static constexpr const char* V1AddressTypeElementVersionDelimiter = AZ::DataPatchInternal::AddressTypeElement::VersionDelimiter; // utf-8 for <middledot>
  580. AZStd::unique_ptr<SerializeContext> m_serializeContext;
  581. DataPatchInternal::AddressTypeSerializer* m_addressTypeSerializer;
  582. };
  583. namespace Patching
  584. {
  585. TEST_F(PatchingTest, UberTest)
  586. {
  587. ObjectToPatch sourceObj;
  588. sourceObj.m_intValue = 101;
  589. sourceObj.m_objectArray.emplace_back();
  590. sourceObj.m_objectArray.emplace_back();
  591. sourceObj.m_objectArray.emplace_back();
  592. sourceObj.m_dynamicField.Set(aznew ContainedObjectNoPersistentId(40));
  593. {
  594. // derived
  595. sourceObj.m_derivedObjectArray.emplace_back();
  596. sourceObj.m_derivedObjectArray.emplace_back();
  597. sourceObj.m_derivedObjectArray.emplace_back();
  598. }
  599. // test generic containers with persistent ID
  600. sourceObj.m_objectArray[0].m_persistentId = 1;
  601. sourceObj.m_objectArray[0].m_data = 201;
  602. sourceObj.m_objectArray[1].m_persistentId = 2;
  603. sourceObj.m_objectArray[1].m_data = 202;
  604. sourceObj.m_objectArray[2].m_persistentId = 3;
  605. sourceObj.m_objectArray[2].m_data = 203;
  606. {
  607. // derived
  608. sourceObj.m_derivedObjectArray[0].m_persistentId = 1;
  609. sourceObj.m_derivedObjectArray[0].m_data = 2010;
  610. sourceObj.m_derivedObjectArray[1].m_persistentId = 2;
  611. sourceObj.m_derivedObjectArray[1].m_data = 2020;
  612. sourceObj.m_derivedObjectArray[2].m_persistentId = 3;
  613. sourceObj.m_derivedObjectArray[2].m_data = 2030;
  614. }
  615. ObjectToPatch targetObj;
  616. targetObj.m_intValue = 121;
  617. targetObj.m_objectArray.emplace_back();
  618. targetObj.m_objectArray.emplace_back();
  619. targetObj.m_objectArray.emplace_back();
  620. targetObj.m_objectArray[0].m_persistentId = 1;
  621. targetObj.m_objectArray[0].m_data = 301;
  622. targetObj.m_dynamicField.Set(aznew ContainedObjectNoPersistentId(50));
  623. {
  624. // derived
  625. targetObj.m_derivedObjectArray.emplace_back();
  626. targetObj.m_derivedObjectArray.emplace_back();
  627. targetObj.m_derivedObjectArray.emplace_back();
  628. targetObj.m_derivedObjectArray[0].m_persistentId = 1;
  629. targetObj.m_derivedObjectArray[0].m_data = 3010;
  630. }
  631. // remove element 2
  632. targetObj.m_objectArray[1].m_persistentId = 3;
  633. targetObj.m_objectArray[1].m_data = 303;
  634. {
  635. // derived
  636. targetObj.m_derivedObjectArray[1].m_persistentId = 3;
  637. targetObj.m_derivedObjectArray[1].m_data = 3030;
  638. }
  639. // add new element
  640. targetObj.m_objectArray[2].m_persistentId = 4;
  641. targetObj.m_objectArray[2].m_data = 304;
  642. {
  643. // derived
  644. targetObj.m_derivedObjectArray[2].m_persistentId = 4;
  645. targetObj.m_derivedObjectArray[2].m_data = 3040;
  646. }
  647. // insert lots of objects without persistent id
  648. targetObj.m_objectArrayNoPersistentId.resize(999);
  649. for (size_t i = 0; i < targetObj.m_objectArrayNoPersistentId.size(); ++i)
  650. {
  651. targetObj.m_objectArrayNoPersistentId[i].m_data = static_cast<int>(i);
  652. }
  653. DataPatch patch;
  654. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  655. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  656. // Compare the generated and original target object
  657. EXPECT_TRUE(generatedObj);
  658. EXPECT_EQ(generatedObj->m_intValue, targetObj.m_intValue);
  659. EXPECT_EQ(generatedObj->m_objectArray.size(), targetObj.m_objectArray.size());
  660. EXPECT_EQ(generatedObj->m_objectArray[0].m_data, targetObj.m_objectArray[0].m_data);
  661. EXPECT_EQ(generatedObj->m_objectArray[0].m_persistentId, targetObj.m_objectArray[0].m_persistentId);
  662. EXPECT_EQ(generatedObj->m_objectArray[1].m_data, targetObj.m_objectArray[1].m_data);
  663. EXPECT_EQ(generatedObj->m_objectArray[1].m_persistentId, targetObj.m_objectArray[1].m_persistentId);
  664. EXPECT_EQ(generatedObj->m_objectArray[2].m_data, targetObj.m_objectArray[2].m_data);
  665. EXPECT_EQ(generatedObj->m_objectArray[2].m_persistentId, targetObj.m_objectArray[2].m_persistentId);
  666. EXPECT_EQ(50, generatedObj->m_dynamicField.Get<ContainedObjectNoPersistentId>()->m_data);
  667. {
  668. // derived
  669. EXPECT_EQ(generatedObj->m_derivedObjectArray.size(), targetObj.m_derivedObjectArray.size());
  670. EXPECT_EQ(generatedObj->m_derivedObjectArray[0].m_data, targetObj.m_derivedObjectArray[0].m_data);
  671. EXPECT_EQ(generatedObj->m_derivedObjectArray[0].m_persistentId, targetObj.m_derivedObjectArray[0].m_persistentId);
  672. EXPECT_EQ(generatedObj->m_derivedObjectArray[1].m_data, targetObj.m_derivedObjectArray[1].m_data);
  673. EXPECT_EQ(generatedObj->m_derivedObjectArray[1].m_persistentId, targetObj.m_derivedObjectArray[1].m_persistentId);
  674. EXPECT_EQ(generatedObj->m_derivedObjectArray[2].m_data, targetObj.m_derivedObjectArray[2].m_data);
  675. EXPECT_EQ(generatedObj->m_derivedObjectArray[2].m_persistentId, targetObj.m_derivedObjectArray[2].m_persistentId);
  676. }
  677. // test that the relative order of elements without persistent ID is preserved
  678. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId.size(), targetObj.m_objectArrayNoPersistentId.size());
  679. for (size_t i = 0; i < targetObj.m_objectArrayNoPersistentId.size(); ++i)
  680. {
  681. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId[i].m_data, targetObj.m_objectArrayNoPersistentId[i].m_data);
  682. }
  683. // \note do we need to add support for base class patching and recover for root elements with proper casting
  684. generatedObj->m_dynamicField.DestroyData(m_serializeContext.get());
  685. targetObj.m_dynamicField.DestroyData(m_serializeContext.get());
  686. sourceObj.m_dynamicField.DestroyData(m_serializeContext.get());
  687. //delete generatedObj;
  688. }
  689. TEST_F(PatchingTest, PatchArray_RemoveAllObjects_DataPatchAppliesCorrectly)
  690. {
  691. // Init Source with arbitrary Persistent IDs and data
  692. ObjectToPatch sourceObj;
  693. sourceObj.m_objectArray.resize(999);
  694. for (size_t i = 0; i < sourceObj.m_objectArray.size(); ++i)
  695. {
  696. sourceObj.m_objectArray[i].m_persistentId = static_cast<int>(i + 10);
  697. sourceObj.m_objectArray[i].m_data = static_cast<int>(i + 200);
  698. }
  699. // Init empty Target
  700. ObjectToPatch targetObj;
  701. // Create and Apply Patch
  702. DataPatch patch;
  703. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  704. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  705. // Test Phase
  706. EXPECT_TRUE(generatedObj);
  707. EXPECT_EQ(generatedObj->m_objectArray.size(), targetObj.m_objectArray.size());
  708. EXPECT_TRUE(targetObj.m_objectArray.empty());
  709. EXPECT_TRUE(generatedObj->m_objectArray.empty());
  710. }
  711. TEST_F(PatchingTest, PatchArray_AddObjects_DataPatchAppliesCorrectly)
  712. {
  713. // Init empty Source
  714. ObjectToPatch sourceObj;
  715. // Init Target with arbitrary Persistent IDs and data
  716. ObjectToPatch targetObj;
  717. targetObj.m_objectArray.resize(999);
  718. for (size_t i = 0; i < targetObj.m_objectArray.size(); ++i)
  719. {
  720. targetObj.m_objectArray[i].m_persistentId = static_cast<int>(i + 10);
  721. targetObj.m_objectArray[i].m_data = static_cast<int>(i + 200);
  722. }
  723. // Create and Apply Patch
  724. DataPatch patch;
  725. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  726. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  727. // Test Phase
  728. EXPECT_TRUE(generatedObj);
  729. EXPECT_EQ(generatedObj->m_objectArray.size(), targetObj.m_objectArray.size());
  730. for (size_t i = 0; i < generatedObj->m_objectArray.size(); ++i)
  731. {
  732. EXPECT_EQ(generatedObj->m_objectArray[i].m_persistentId, targetObj.m_objectArray[i].m_persistentId);
  733. EXPECT_EQ(generatedObj->m_objectArray[i].m_data, targetObj.m_objectArray[i].m_data);
  734. }
  735. }
  736. TEST_F(PatchingTest, PatchArray_EditAllObjects_DataPatchAppliesCorrectly)
  737. {
  738. // Init Source and Target with arbitrary Persistent IDs (the same) and data (different)
  739. ObjectToPatch sourceObj;
  740. sourceObj.m_objectArray.resize(999);
  741. ObjectToPatch targetObj;
  742. targetObj.m_objectArray.resize(999);
  743. for (size_t i = 0; i < sourceObj.m_objectArray.size(); ++i)
  744. {
  745. sourceObj.m_objectArray[i].m_persistentId = static_cast<int>(i + 10);
  746. sourceObj.m_objectArray[i].m_data = static_cast<int>(i + 200);
  747. // Keep the Persistent IDs the same but change the data
  748. targetObj.m_objectArray[i].m_persistentId = sourceObj.m_objectArray[i].m_persistentId;
  749. targetObj.m_objectArray[i].m_data = sourceObj.m_objectArray[i].m_data + 100;
  750. }
  751. // Create and Apply Patch
  752. DataPatch patch;
  753. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  754. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  755. // Test Phase
  756. EXPECT_TRUE(generatedObj);
  757. EXPECT_EQ(generatedObj->m_objectArray.size(), targetObj.m_objectArray.size());
  758. for (size_t i = 0; i < generatedObj->m_objectArray.size(); ++i)
  759. {
  760. EXPECT_EQ(generatedObj->m_objectArray[i].m_persistentId, targetObj.m_objectArray[i].m_persistentId);
  761. EXPECT_EQ(generatedObj->m_objectArray[i].m_data, targetObj.m_objectArray[i].m_data);
  762. }
  763. }
  764. TEST_F(PatchingTest, PatchArray_AddRemoveEdit_DataPatchAppliesCorrectly)
  765. {
  766. // Init Source
  767. ObjectToPatch sourceObj;
  768. sourceObj.m_objectArray.resize(3);
  769. sourceObj.m_objectArray[0].m_persistentId = 1;
  770. sourceObj.m_objectArray[0].m_data = 201;
  771. sourceObj.m_objectArray[1].m_persistentId = 2;
  772. sourceObj.m_objectArray[1].m_data = 202;
  773. sourceObj.m_objectArray[2].m_persistentId = 3;
  774. sourceObj.m_objectArray[2].m_data = 203;
  775. // Init Target
  776. ObjectToPatch targetObj;
  777. targetObj.m_objectArray.resize(4);
  778. // Edit ID 1
  779. targetObj.m_objectArray[0].m_persistentId = 1;
  780. targetObj.m_objectArray[0].m_data = 301;
  781. // Remove ID 2, do not edit ID 3
  782. targetObj.m_objectArray[1].m_persistentId = 3;
  783. targetObj.m_objectArray[1].m_data = 203;
  784. // Add ID 4 and 5
  785. targetObj.m_objectArray[2].m_persistentId = 4;
  786. targetObj.m_objectArray[2].m_data = 304;
  787. targetObj.m_objectArray[3].m_persistentId = 5;
  788. targetObj.m_objectArray[3].m_data = 305;
  789. // Create and Apply Patch
  790. DataPatch patch;
  791. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  792. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  793. // Test Phase
  794. EXPECT_TRUE(generatedObj);
  795. EXPECT_EQ(generatedObj->m_objectArray.size(), targetObj.m_objectArray.size());
  796. EXPECT_EQ(generatedObj->m_objectArray[0].m_persistentId, targetObj.m_objectArray[0].m_persistentId);
  797. EXPECT_EQ(generatedObj->m_objectArray[0].m_data, targetObj.m_objectArray[0].m_data);
  798. EXPECT_EQ(generatedObj->m_objectArray[1].m_persistentId, targetObj.m_objectArray[1].m_persistentId);
  799. EXPECT_EQ(generatedObj->m_objectArray[1].m_data, targetObj.m_objectArray[1].m_data);
  800. EXPECT_EQ(generatedObj->m_objectArray[2].m_persistentId, targetObj.m_objectArray[2].m_persistentId);
  801. EXPECT_EQ(generatedObj->m_objectArray[2].m_data, targetObj.m_objectArray[2].m_data);
  802. EXPECT_EQ(generatedObj->m_objectArray[3].m_persistentId, targetObj.m_objectArray[3].m_persistentId);
  803. EXPECT_EQ(generatedObj->m_objectArray[3].m_data, targetObj.m_objectArray[3].m_data);
  804. }
  805. TEST_F(PatchingTest, PatchArray_ObjectsHaveNoPersistentId_RemoveAllObjects_DataPatchAppliesCorrectly)
  806. {
  807. // Init Source
  808. ObjectToPatch sourceObj;
  809. sourceObj.m_objectArrayNoPersistentId.resize(999);
  810. for (size_t i = 0; i < sourceObj.m_objectArrayNoPersistentId.size(); ++i)
  811. {
  812. sourceObj.m_objectArrayNoPersistentId[i].m_data = static_cast<int>(i);
  813. }
  814. // Init empty Target
  815. ObjectToPatch targetObj;
  816. // Create and Apply Patch
  817. DataPatch patch;
  818. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  819. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  820. // Test Phase
  821. EXPECT_TRUE(generatedObj);
  822. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId.size(), targetObj.m_objectArrayNoPersistentId.size());
  823. EXPECT_TRUE(targetObj.m_objectArrayNoPersistentId.empty());
  824. EXPECT_TRUE(generatedObj->m_objectArrayNoPersistentId.empty());
  825. }
  826. TEST_F(PatchingTest, PatchArray_ObjectsHaveNoPersistentId_AddObjects_DataPatchAppliesCorrectly)
  827. {
  828. // Init empty Source
  829. ObjectToPatch sourceObj;
  830. // Init Target
  831. ObjectToPatch targetObj;
  832. targetObj.m_objectArrayNoPersistentId.resize(999);
  833. for (size_t i = 0; i < targetObj.m_objectArrayNoPersistentId.size(); ++i)
  834. {
  835. targetObj.m_objectArrayNoPersistentId[i].m_data = static_cast<int>(i);
  836. }
  837. // Create and Apply Patch
  838. DataPatch patch;
  839. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  840. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  841. // Test Phase
  842. EXPECT_TRUE(generatedObj);
  843. EXPECT_THAT(generatedObj->m_objectArrayNoPersistentId, ::testing::Pointwise(::testing::Truly([](auto arg){ return testing::get<0>(arg).m_data == testing::get<1>(arg).m_data; }), targetObj.m_objectArrayNoPersistentId));
  844. }
  845. TEST_F(PatchingTest, SimpleClassContainingVectorOfInts)
  846. {
  847. SimpleClassContainingVectorOfInts::Reflect(*m_serializeContext.get());
  848. // Init empty Source
  849. SimpleClassContainingVectorOfInts sourceObj;
  850. // Init Target
  851. SimpleClassContainingVectorOfInts targetObj;
  852. targetObj.m_id.resize(20);
  853. for (size_t i = 0; i < targetObj.m_id.size(); ++i)
  854. {
  855. targetObj.m_id[i] = 0;
  856. }
  857. // Create and Apply Patch
  858. DataPatch patch;
  859. EXPECT_TRUE(patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get()));
  860. AZStd::unique_ptr<SimpleClassContainingVectorOfInts> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  861. // Test Phase
  862. EXPECT_TRUE(generatedObj);
  863. EXPECT_THAT(generatedObj->m_id, ::testing::Pointwise(::testing::Truly([](auto arg){ return testing::get<0>(arg) == testing::get<1>(arg); }), targetObj.m_id));
  864. }
  865. TEST_F(PatchingTest, PatchArray_ObjectsHaveNoPersistentId_EditAllObjects_DataPatchAppliesCorrectly)
  866. {
  867. // Init Source
  868. ObjectToPatch sourceObj;
  869. sourceObj.m_objectArrayNoPersistentId.resize(999);
  870. for (size_t i = 0; i < sourceObj.m_objectArrayNoPersistentId.size(); ++i)
  871. {
  872. sourceObj.m_objectArrayNoPersistentId[i].m_data = static_cast<int>(i);
  873. }
  874. // Init Target
  875. ObjectToPatch targetObj;
  876. targetObj.m_objectArrayNoPersistentId.resize(999);
  877. for (size_t i = 0; i < targetObj.m_objectArrayNoPersistentId.size(); ++i)
  878. {
  879. targetObj.m_objectArrayNoPersistentId[i].m_data = static_cast<int>(i + 1);
  880. }
  881. // Create and Apply Patch
  882. DataPatch patch;
  883. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  884. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  885. // Test Phase
  886. EXPECT_TRUE(generatedObj);
  887. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId.size(), targetObj.m_objectArrayNoPersistentId.size());
  888. for (size_t i = 0; i < targetObj.m_objectArrayNoPersistentId.size(); ++i)
  889. {
  890. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId[i].m_data, targetObj.m_objectArrayNoPersistentId[i].m_data);
  891. }
  892. }
  893. TEST_F(PatchingTest, PatchArray_ObjectsHaveNoPersistentId_RemoveEdit_DataPatchAppliesCorrectly)
  894. {
  895. // Init Source
  896. ObjectToPatch sourceObj;
  897. sourceObj.m_objectArrayNoPersistentId.resize(4);
  898. sourceObj.m_objectArrayNoPersistentId[0].m_data = static_cast<int>(1000);
  899. sourceObj.m_objectArrayNoPersistentId[1].m_data = static_cast<int>(1001);
  900. sourceObj.m_objectArrayNoPersistentId[2].m_data = static_cast<int>(1002);
  901. sourceObj.m_objectArrayNoPersistentId[3].m_data = static_cast<int>(1003);
  902. // Init Target
  903. ObjectToPatch targetObj;
  904. targetObj.m_objectArrayNoPersistentId.resize(2);
  905. targetObj.m_objectArrayNoPersistentId[0].m_data = static_cast<int>(2000);
  906. targetObj.m_objectArrayNoPersistentId[1].m_data = static_cast<int>(2001);
  907. // Create and Apply Patch
  908. DataPatch patch;
  909. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  910. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  911. // Test Phase
  912. EXPECT_TRUE(generatedObj);
  913. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId.size(), 2);
  914. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId.size(), targetObj.m_objectArrayNoPersistentId.size());
  915. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId[0].m_data, targetObj.m_objectArrayNoPersistentId[0].m_data);
  916. EXPECT_EQ(generatedObj->m_objectArrayNoPersistentId[1].m_data, targetObj.m_objectArrayNoPersistentId[1].m_data);
  917. }
  918. TEST_F(PatchingTest, PatchUnorderedMap_ObjectsHaveNoPersistentId_RemoveAllObjects_DataPatchAppliesCorrectly)
  919. {
  920. // test generic containers without persistent ID (by index)
  921. // Init Source
  922. ObjectToPatch sourceObj;
  923. sourceObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(401));
  924. sourceObj.m_objectMap.emplace(2, aznew ContainedObjectNoPersistentId(402));
  925. sourceObj.m_objectMap.emplace(3, aznew ContainedObjectNoPersistentId(403));
  926. sourceObj.m_objectMap.emplace(4, aznew ContainedObjectNoPersistentId(404));
  927. // Init empty Target
  928. ObjectToPatch targetObj;
  929. // Create and Apply Patch
  930. DataPatch patch;
  931. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  932. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  933. // Test Phase
  934. EXPECT_TRUE(generatedObj);
  935. EXPECT_EQ(generatedObj->m_objectMap.size(), targetObj.m_objectMap.size());
  936. EXPECT_TRUE(targetObj.m_objectMap.empty());
  937. EXPECT_TRUE(generatedObj->m_objectMap.empty());
  938. }
  939. TEST_F(PatchingTest, PatchUnorderedMap_ObjectsHaveNoPersistentId_AddObjects_DataPatchAppliesCorrectly)
  940. {
  941. // test generic containers without persistent ID (by index)
  942. // Init empty Source
  943. ObjectToPatch sourceObj;
  944. // Init Target
  945. ObjectToPatch targetObj;
  946. targetObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(401));
  947. targetObj.m_objectMap.emplace(2, aznew ContainedObjectNoPersistentId(402));
  948. targetObj.m_objectMap.emplace(3, aznew ContainedObjectNoPersistentId(403));
  949. targetObj.m_objectMap.emplace(4, aznew ContainedObjectNoPersistentId(404));
  950. // Create and Apply Patch
  951. DataPatch patch;
  952. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  953. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  954. // Test Phase
  955. EXPECT_TRUE(generatedObj);
  956. EXPECT_EQ(generatedObj->m_objectMap.size(), targetObj.m_objectMap.size());
  957. EXPECT_EQ(generatedObj->m_objectMap[1]->m_data, targetObj.m_objectMap[1]->m_data);
  958. EXPECT_EQ(generatedObj->m_objectMap[2]->m_data, targetObj.m_objectMap[2]->m_data);
  959. EXPECT_EQ(generatedObj->m_objectMap[3]->m_data, targetObj.m_objectMap[3]->m_data);
  960. EXPECT_EQ(generatedObj->m_objectMap[4]->m_data, targetObj.m_objectMap[4]->m_data);
  961. }
  962. TEST_F(PatchingTest, PatchUnorderedMap_ObjectsHaveNoPersistentId_EditAllObjects_DataPatchAppliesCorrectly)
  963. {
  964. // test generic containers without persistent ID (by index)
  965. // Init Source
  966. ObjectToPatch sourceObj;
  967. sourceObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(401));
  968. sourceObj.m_objectMap.emplace(2, aznew ContainedObjectNoPersistentId(402));
  969. sourceObj.m_objectMap.emplace(3, aznew ContainedObjectNoPersistentId(403));
  970. sourceObj.m_objectMap.emplace(4, aznew ContainedObjectNoPersistentId(404));
  971. // Init Target
  972. ObjectToPatch targetObj;
  973. targetObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(501));
  974. targetObj.m_objectMap.emplace(2, aznew ContainedObjectNoPersistentId(502));
  975. targetObj.m_objectMap.emplace(3, aznew ContainedObjectNoPersistentId(503));
  976. targetObj.m_objectMap.emplace(4, aznew ContainedObjectNoPersistentId(504));
  977. // Create and Apply Patch
  978. DataPatch patch;
  979. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  980. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  981. // Test Phase
  982. EXPECT_TRUE(generatedObj);
  983. EXPECT_EQ(generatedObj->m_objectMap.size(), targetObj.m_objectMap.size());
  984. EXPECT_EQ(generatedObj->m_objectMap[1]->m_data, targetObj.m_objectMap[1]->m_data);
  985. EXPECT_EQ(generatedObj->m_objectMap[2]->m_data, targetObj.m_objectMap[2]->m_data);
  986. EXPECT_EQ(generatedObj->m_objectMap[3]->m_data, targetObj.m_objectMap[3]->m_data);
  987. EXPECT_EQ(generatedObj->m_objectMap[4]->m_data, targetObj.m_objectMap[4]->m_data);
  988. }
  989. TEST_F(PatchingTest, PatchUnorderedMap_ObjectsHaveNoPersistentId_AddRemoveEdit_DataPatchAppliesCorrectly)
  990. {
  991. // test generic containers without persistent ID (by index)
  992. // Init Source
  993. ObjectToPatch sourceObj;
  994. sourceObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(401));
  995. sourceObj.m_objectMap.emplace(2, aznew ContainedObjectNoPersistentId(402));
  996. sourceObj.m_objectMap.emplace(3, aznew ContainedObjectNoPersistentId(403));
  997. sourceObj.m_objectMap.emplace(4, aznew ContainedObjectNoPersistentId(404));
  998. // Init Target
  999. ObjectToPatch targetObj;
  1000. // This will mark the object at index 1 as an edit, objects 2-4 as removed, and 5 as an addition
  1001. targetObj.m_objectMap.emplace(1, aznew ContainedObjectNoPersistentId(501));
  1002. targetObj.m_objectMap.emplace(5, aznew ContainedObjectNoPersistentId(405));
  1003. // Create and Apply Patch
  1004. DataPatch patch;
  1005. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1006. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1007. // Test Phase
  1008. EXPECT_TRUE(generatedObj);
  1009. EXPECT_EQ(generatedObj->m_objectMap.size(), 2);
  1010. EXPECT_EQ(generatedObj->m_objectMap.size(), targetObj.m_objectMap.size());
  1011. EXPECT_EQ(generatedObj->m_objectMap[1]->m_data, targetObj.m_objectMap[1]->m_data);
  1012. EXPECT_EQ(generatedObj->m_objectMap[5]->m_data, targetObj.m_objectMap[5]->m_data);
  1013. }
  1014. TEST_F(PatchingTest, ReplaceRootElement_DifferentObjects_DataPatchAppliesCorrectly)
  1015. {
  1016. ObjectToPatch obj1;
  1017. DifferentObjectToPatch obj2;
  1018. obj1.m_intValue = 99;
  1019. obj2.m_data = 3.33f;
  1020. DataPatch patch1;
  1021. patch1.Create(static_cast<CommonPatch*>(&obj1), static_cast<CommonPatch*>(&obj2), DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get()); // cast to base classes
  1022. DifferentObjectToPatch* obj2Generated = patch1.Apply<DifferentObjectToPatch>(&obj1, m_serializeContext.get());
  1023. EXPECT_EQ(obj2.m_data, obj2Generated->m_data);
  1024. delete obj2Generated;
  1025. }
  1026. TEST_F(PatchingTest, CompareWithGenerics_DifferentObjects_DataPatchAppliesCorrectly)
  1027. {
  1028. ObjectsWithGenerics sourceGeneric;
  1029. sourceGeneric.m_string = "Hello";
  1030. ObjectsWithGenerics targetGeneric;
  1031. targetGeneric.m_string = "Ola";
  1032. DataPatch genericPatch;
  1033. genericPatch.Create(&sourceGeneric, &targetGeneric, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1034. ObjectsWithGenerics* targerGenericGenerated = genericPatch.Apply(&sourceGeneric, m_serializeContext.get());
  1035. EXPECT_EQ(targetGeneric.m_string, targerGenericGenerated->m_string);
  1036. delete targerGenericGenerated;
  1037. }
  1038. TEST_F(PatchingTest, CompareIdentical_DataPatchIsEmpty)
  1039. {
  1040. ObjectToPatch sourceObj;
  1041. ObjectToPatch targetObj;
  1042. // Patch without overrides should be empty
  1043. DataPatch patch;
  1044. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1045. EXPECT_FALSE(patch.IsData());
  1046. }
  1047. TEST_F(PatchingTest, CompareIdenticalWithForceOverride_DataPatchHasData)
  1048. {
  1049. ObjectToPatch sourceObj;
  1050. ObjectToPatch targetObj;
  1051. DataPatch::AddressType forceOverrideAddress;
  1052. forceOverrideAddress.emplace_back(AZ_CRC_CE("m_intValue"));
  1053. DataPatch::FlagsMap sourceFlagsMap;
  1054. DataPatch::FlagsMap targetFlagsMap;
  1055. targetFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::ForceOverrideSet);
  1056. DataPatch patch;
  1057. patch.Create(&sourceObj, &targetObj, sourceFlagsMap, targetFlagsMap, m_serializeContext.get());
  1058. EXPECT_TRUE(patch.IsData());
  1059. }
  1060. TEST_F(PatchingTest, ChangeSourceAfterForceOverride_TargetDataUnchanged)
  1061. {
  1062. ObjectToPatch sourceObj;
  1063. ObjectToPatch targetObj;
  1064. DataPatch::AddressType forceOverrideAddress;
  1065. forceOverrideAddress.emplace_back(AZ_CRC_CE("m_intValue"));
  1066. DataPatch::FlagsMap sourceFlagsMap;
  1067. DataPatch::FlagsMap targetFlagsMap;
  1068. targetFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::ForceOverrideSet);
  1069. DataPatch patch;
  1070. patch.Create(&sourceObj, &targetObj, sourceFlagsMap, targetFlagsMap, m_serializeContext.get());
  1071. // change source after patch is created
  1072. sourceObj.m_intValue = 5;
  1073. AZStd::unique_ptr<ObjectToPatch> targetObj2(patch.Apply(&sourceObj, m_serializeContext.get()));
  1074. EXPECT_EQ(targetObj.m_intValue, targetObj2->m_intValue);
  1075. }
  1076. TEST_F(PatchingTest, ForceOverrideAndPreventOverrideBothSet_DataPatchIsEmpty)
  1077. {
  1078. ObjectToPatch sourceObj;
  1079. ObjectToPatch targetObj;
  1080. targetObj.m_intValue = 43;
  1081. DataPatch::AddressType forceOverrideAddress;
  1082. forceOverrideAddress.emplace_back(AZ_CRC_CE("m_intValue"));
  1083. DataPatch::FlagsMap sourceFlagsMap;
  1084. sourceFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::PreventOverrideSet);
  1085. DataPatch::FlagsMap targetFlagsMap;
  1086. targetFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::ForceOverrideSet);
  1087. DataPatch patch;
  1088. patch.Create(&sourceObj, &targetObj, sourceFlagsMap, targetFlagsMap, m_serializeContext.get());
  1089. EXPECT_FALSE(patch.IsData());
  1090. }
  1091. TEST_F(PatchingTest, PreventOverrideOnSource_BlocksValueFromPatch)
  1092. {
  1093. // targetObj is different from sourceObj
  1094. ObjectToPatch sourceObj;
  1095. ObjectToPatch targetObj;
  1096. targetObj.m_intValue = 5;
  1097. // create patch from sourceObj -> targetObj
  1098. DataPatch patch;
  1099. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1100. // create flags that prevent m_intValue from being patched
  1101. DataPatch::AddressType forceOverrideAddress;
  1102. forceOverrideAddress.emplace_back(AZ_CRC_CE("m_intValue"));
  1103. DataPatch::FlagsMap sourceFlagsMap;
  1104. sourceFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::PreventOverrideSet);
  1105. DataPatch::FlagsMap targetFlagsMap;
  1106. // m_intValue should be the same as it was in sourceObj
  1107. AZStd::unique_ptr<ObjectToPatch> targetObj2(patch.Apply(&sourceObj, m_serializeContext.get(), ObjectStream::FilterDescriptor(), sourceFlagsMap, targetFlagsMap));
  1108. EXPECT_EQ(sourceObj.m_intValue, targetObj2->m_intValue);
  1109. }
  1110. TEST_F(PatchingTest, PreventOverrideOnTarget_DoesntAffectPatching)
  1111. {
  1112. // targetObj is different from sourceObj
  1113. ObjectToPatch sourceObj;
  1114. ObjectToPatch targetObj;
  1115. targetObj.m_intValue = 5;
  1116. // create patch from sourceObj -> targetObj
  1117. DataPatch patch;
  1118. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1119. // create flags that prevent m_intValue from being patched, but put them on the target instead of source
  1120. DataPatch::AddressType forceOverrideAddress;
  1121. forceOverrideAddress.emplace_back(AZ_CRC_CE("m_intValue"));
  1122. DataPatch::FlagsMap sourceFlagsMap;
  1123. DataPatch::FlagsMap targetFlagsMap;
  1124. targetFlagsMap.emplace(forceOverrideAddress, DataPatch::Flag::PreventOverrideSet);
  1125. // m_intValue should have been patched
  1126. AZStd::unique_ptr<ObjectToPatch> targetObj2(patch.Apply(&sourceObj, m_serializeContext.get(), ObjectStream::FilterDescriptor(), sourceFlagsMap, targetFlagsMap));
  1127. EXPECT_EQ(targetObj.m_intValue, targetObj2->m_intValue);
  1128. }
  1129. TEST_F(PatchingTest, PatchNullptrInSource)
  1130. {
  1131. ObjectWithPointer sourceObj;
  1132. sourceObj.m_int = 7;
  1133. ObjectWithPointer targetObj;
  1134. targetObj.m_int = 8;
  1135. targetObj.m_pointerInt = new AZ::s32(-1);
  1136. DataPatch patch;
  1137. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1138. ObjectWithPointer* patchedTargetObj = patch.Apply(&sourceObj, m_serializeContext.get());
  1139. EXPECT_EQ(targetObj.m_int, patchedTargetObj->m_int);
  1140. EXPECT_NE(nullptr, patchedTargetObj->m_pointerInt);
  1141. EXPECT_EQ(*targetObj.m_pointerInt, *patchedTargetObj->m_pointerInt);
  1142. delete targetObj.m_pointerInt;
  1143. azdestroy(patchedTargetObj->m_pointerInt);
  1144. delete patchedTargetObj;
  1145. }
  1146. TEST_F(PatchingTest, PatchNullptrInTarget)
  1147. {
  1148. ObjectWithPointer sourceObj;
  1149. sourceObj.m_int = 20;
  1150. sourceObj.m_pointerInt = new AZ::s32(500);
  1151. ObjectWithPointer targetObj;
  1152. targetObj.m_int = 23054;
  1153. DataPatch patch;
  1154. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1155. ObjectWithPointer* patchedTargetObj = patch.Apply(&sourceObj, m_serializeContext.get());
  1156. EXPECT_EQ(targetObj.m_int, patchedTargetObj->m_int);
  1157. EXPECT_EQ(nullptr, patchedTargetObj->m_pointerInt);
  1158. delete sourceObj.m_pointerInt;
  1159. delete patchedTargetObj;
  1160. }
  1161. // prove that properly deprecated container elements are removed and do not leave nulls behind.
  1162. TEST_F(PatchingTest, DeprecatedContainerElements_AreRemoved)
  1163. {
  1164. ObjectBaseClass::Reflect(*m_serializeContext);
  1165. ObjectDerivedClass1::Reflect(*m_serializeContext);
  1166. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1167. ObjectWithVectorOfBaseClasses::Reflect(*m_serializeContext);
  1168. // step 1: Make a patch that includes both classes.
  1169. ObjectWithVectorOfBaseClasses sourceObject;
  1170. ObjectWithVectorOfBaseClasses targetObject;
  1171. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1());
  1172. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1173. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1174. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1175. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1()); // <-- we expect to see this second one, it should not be lost
  1176. DataPatch patch;
  1177. patch.Create(&sourceObject, &targetObject, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1178. // step 2: DerivedClass2 no longer exists:
  1179. m_serializeContext->EnableRemoveReflection();
  1180. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1181. m_serializeContext->DisableRemoveReflection();
  1182. m_serializeContext->ClassDeprecate("ObjectDerivedClass2", azrtti_typeid<ObjectDerivedClass2>());
  1183. // generate a patch which will turn a given source object into the targetObject.
  1184. ObjectWithVectorOfBaseClasses* patchedTargetObj = patch.Apply(&sourceObject, m_serializeContext.get());
  1185. // at this point, the patched target object should only have ObjectDerivedClass1s on it.
  1186. // two of them exactly. There should be no other types and there should be no null holes in it.
  1187. EXPECT_EQ(patchedTargetObj->m_vectorOfBaseClasses.size(), 2);
  1188. for (auto element : patchedTargetObj->m_vectorOfBaseClasses)
  1189. {
  1190. EXPECT_EQ(azrtti_typeid(*element), azrtti_typeid<ObjectDerivedClass1>() );
  1191. }
  1192. delete patchedTargetObj;
  1193. }
  1194. // prove that unreadable container elements (ie, no deprecation info) generate warnings but also
  1195. // do not leave nulls behind.
  1196. TEST_F(PatchingTest, UnreadableContainerElements_WithNoDeprecation_GenerateWarning_AreRemoved)
  1197. {
  1198. ObjectBaseClass::Reflect(*m_serializeContext);
  1199. ObjectDerivedClass1::Reflect(*m_serializeContext);
  1200. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1201. ObjectWithVectorOfBaseClasses::Reflect(*m_serializeContext);
  1202. // Make a patch that includes both classes.
  1203. ObjectWithVectorOfBaseClasses sourceObject;
  1204. ObjectWithVectorOfBaseClasses targetObject;
  1205. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1());
  1206. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1207. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1208. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1209. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1()); // <-- we expect to see this second one, it should not be lost
  1210. DataPatch patch;
  1211. patch.Create(&sourceObject, &targetObject, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1212. // Remove DerivedClass2 from the serialize context:
  1213. m_serializeContext->EnableRemoveReflection();
  1214. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1215. m_serializeContext->DisableRemoveReflection();
  1216. // apply the patch despite it containing deprecated things with no deprecation tag, expect 1 error per unknown instance:
  1217. AZ_TEST_START_TRACE_SUPPRESSION;
  1218. ObjectWithVectorOfBaseClasses* patchedTargetObj = patch.Apply(&sourceObject, m_serializeContext.get());
  1219. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  1220. // at this point, the patched target object should only have ObjectDerivedClass1s on it.
  1221. // two of them exactly. There should be no other types and there should be no null holes in it.
  1222. EXPECT_EQ(patchedTargetObj->m_vectorOfBaseClasses.size(), 2);
  1223. for (auto element : patchedTargetObj->m_vectorOfBaseClasses)
  1224. {
  1225. EXPECT_EQ(azrtti_typeid(*element), azrtti_typeid<ObjectDerivedClass1>() );
  1226. }
  1227. delete patchedTargetObj;
  1228. }
  1229. // note that the entire conversion subsystem is based on loading through an ObjectStream, not a direct patch.
  1230. // It is not a real use case to deprecate a class during execution and then expect data patch upgrading to function.
  1231. // Instead, deprecated classes always come from data "at rest" such as on disk / network stream, which means
  1232. // they come via ObjectStream, which does perform conversion and has its own tests.
  1233. // This test is just to ensure that when you do load a patch (Using ObjectStream) and elements in that patch have been
  1234. // deprecated, it does not cause unexpected errors.
  1235. TEST_F(PatchingTest, UnreadableContainerElements_WithDeprecationConverters_AreConverted)
  1236. {
  1237. ObjectBaseClass::Reflect(*m_serializeContext);
  1238. ObjectDerivedClass1::Reflect(*m_serializeContext);
  1239. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1240. ObjectDerivedClass3::Reflect(*m_serializeContext);
  1241. ObjectWithVectorOfBaseClasses::Reflect(*m_serializeContext);
  1242. // step 1: Make a patch that includes deprecated classes.
  1243. ObjectWithVectorOfBaseClasses sourceObject;
  1244. ObjectWithVectorOfBaseClasses targetObject;
  1245. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1());
  1246. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1247. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass1());
  1248. targetObject.m_vectorOfBaseClasses.push_back(new ObjectDerivedClass2());
  1249. DataPatch patch;
  1250. patch.Create(&sourceObject, &targetObject, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1251. // save the patch itself to a stream.
  1252. AZStd::vector<char> charBuffer;
  1253. AZ::IO::ByteContainerStream<AZStd::vector<char> > containerStream(&charBuffer);
  1254. bool success = AZ::Utils::SaveObjectToStream(containerStream, AZ::ObjectStream::ST_XML, &patch, m_serializeContext.get());
  1255. EXPECT_TRUE(success);
  1256. // step 2: DerivedClass2 no longer exists:
  1257. m_serializeContext->EnableRemoveReflection();
  1258. ObjectDerivedClass2::Reflect(*m_serializeContext);
  1259. m_serializeContext->DisableRemoveReflection();
  1260. m_serializeContext->ClassDeprecate("Dummy UUID", azrtti_typeid<ObjectDerivedClass2>(), ConvertDerivedClass2ToDerivedClass3);
  1261. // load it from the container
  1262. DataPatch loadedPatch;
  1263. // it should generate no warnings but the deprecated ones should not be there.
  1264. success = AZ::Utils::LoadObjectFromBufferInPlace(charBuffer.data(), charBuffer.size(), loadedPatch, m_serializeContext.get());
  1265. EXPECT_TRUE(success);
  1266. // patch the original source object with the new patch which was loaded:
  1267. ObjectWithVectorOfBaseClasses* patchedTargetObj = loadedPatch.Apply(&sourceObject, m_serializeContext.get());
  1268. // prove that all deprecated classes were converted and order did not shuffle:
  1269. ASSERT_EQ(patchedTargetObj->m_vectorOfBaseClasses.size(), 4);
  1270. EXPECT_EQ(azrtti_typeid(patchedTargetObj->m_vectorOfBaseClasses[0]), azrtti_typeid<ObjectDerivedClass1>() );
  1271. EXPECT_EQ(azrtti_typeid(patchedTargetObj->m_vectorOfBaseClasses[1]), azrtti_typeid<ObjectDerivedClass3>() );
  1272. EXPECT_EQ(azrtti_typeid(patchedTargetObj->m_vectorOfBaseClasses[2]), azrtti_typeid<ObjectDerivedClass1>() );
  1273. EXPECT_EQ(azrtti_typeid(patchedTargetObj->m_vectorOfBaseClasses[3]), azrtti_typeid<ObjectDerivedClass3>() );
  1274. delete patchedTargetObj;
  1275. }
  1276. TEST_F(PatchingTest, PatchDistinctNullptrSourceTarget)
  1277. {
  1278. ObjectWithMultiPointers sourceObj;
  1279. sourceObj.m_int = 54;
  1280. sourceObj.m_pointerInt = new AZ::s32(500);
  1281. ObjectWithMultiPointers targetObj;
  1282. targetObj.m_int = -2493;
  1283. targetObj.m_pointerFloat = new float(3.14f);
  1284. DataPatch patch;
  1285. patch.Create(&sourceObj, &targetObj, DataPatch::FlagsMap(), DataPatch::FlagsMap(), m_serializeContext.get());
  1286. ObjectWithMultiPointers* patchedTargetObj = patch.Apply(&sourceObj, m_serializeContext.get());
  1287. EXPECT_EQ(targetObj.m_int, patchedTargetObj->m_int);
  1288. EXPECT_EQ(nullptr, patchedTargetObj->m_pointerInt);
  1289. EXPECT_NE(nullptr, patchedTargetObj->m_pointerFloat);
  1290. EXPECT_EQ(*targetObj.m_pointerFloat, *patchedTargetObj->m_pointerFloat);
  1291. delete sourceObj.m_pointerInt;
  1292. delete targetObj.m_pointerFloat;
  1293. delete patchedTargetObj->m_pointerInt;
  1294. azdestroy(patchedTargetObj->m_pointerFloat);
  1295. delete patchedTargetObj;
  1296. }
  1297. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidValueOverride_ApplySucceeds_FT)
  1298. {
  1299. // A Legacy DataPatch containing an int set to 150
  1300. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1301. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1302. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1303. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1304. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1305. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1306. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000960000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1307. </Class>
  1308. </Class>
  1309. </Class>
  1310. </ObjectStream>
  1311. )";
  1312. // Load the patch from XML
  1313. // This triggers the Legacy DataPatch converter
  1314. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1315. // Apply provides the remaining class data to complete the conversion
  1316. DataPatch patch;
  1317. LoadPatchFromXML(legacyPatchXML, patch);
  1318. // Apply the patch and complete conversion
  1319. ObjectToPatch sourceObj;
  1320. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1321. // Verify the patch applied as expected
  1322. EXPECT_TRUE(generatedObj);
  1323. EXPECT_EQ(generatedObj->m_intValue, 150);
  1324. }
  1325. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidValueOverride_LegacyPatchUsesObjectStreamVersion1_ApplySucceeds_FT)
  1326. {
  1327. // A Legacy DataPatch containing an int set to 180 and using ObjectStream V1 types for the unordered map, pair, and bytestream.
  1328. // Note: Does not use legacy types in the patch themselves (EX: a patched AZStd::string will use it's V3 typeId not V1)
  1329. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="1">
  1330. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1331. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1332. <Class name="AZStd::unordered_map" field="m_patch" type="{18456A80-63CC-40C5-BF16-6AF94F9A9ECC}">
  1333. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1334. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1335. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000B40000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1336. </Class>
  1337. </Class>
  1338. </Class>
  1339. </ObjectStream>
  1340. )";
  1341. // Load the patch from XML
  1342. // This triggers the Legacy DataPatch converter
  1343. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1344. // Apply provides the remaining class data to complete the conversion
  1345. DataPatch patch;
  1346. LoadPatchFromXML(legacyPatchXML, patch);
  1347. // Apply the patch and complete conversion
  1348. ObjectToPatch sourceObj;
  1349. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1350. // Verify the patch applied as expected
  1351. EXPECT_TRUE(generatedObj);
  1352. EXPECT_EQ(generatedObj->m_intValue, 180);
  1353. }
  1354. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidPointerOverride_ApplySucceeds_FT)
  1355. {
  1356. // A Legacy DataPatch containing a pointer to an int set to 56
  1357. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1358. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1359. <Class name="AZ::Uuid" field="m_targetClassId" value="{D1FD3240-A7C5-4EA3-8E55-CD18193162B8}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1360. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1361. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1362. <Class name="AddressType" field="value1" value="F01997AC00000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1363. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000380000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1364. </Class>
  1365. </Class>
  1366. </Class>
  1367. </ObjectStream>
  1368. )";
  1369. // Load the patch from XML
  1370. // This triggers the Legacy DataPatch converter
  1371. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1372. // Apply provides the remaining class data to complete the conversion
  1373. DataPatch patch;
  1374. LoadPatchFromXML(legacyPatchXML, patch);
  1375. // Apply the patch and complete conversion
  1376. ObjectWithPointer sourceObj;
  1377. AZStd::unique_ptr<ObjectWithPointer> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1378. // Verify the patch applied as expected
  1379. EXPECT_TRUE(generatedObj);
  1380. EXPECT_TRUE(generatedObj->m_pointerInt);
  1381. EXPECT_EQ(*generatedObj->m_pointerInt, 56);
  1382. azdestroy(generatedObj->m_pointerInt);
  1383. }
  1384. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidPointerOverride_LegacyPatchUsesObjectStreamVersion1_ApplySucceeds_FT)
  1385. {
  1386. // A Legacy DataPatch containing a pointer to an int set to 74 and using ObjectStream V1 types for the unordered map, pair, and bytestream.
  1387. // Note: Does not use legacy types in the patch themselves (EX: a patched AZStd::string will use it's V3 typeId not V1)
  1388. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="1">
  1389. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1390. <Class name="AZ::Uuid" field="m_targetClassId" value="{D1FD3240-A7C5-4EA3-8E55-CD18193162B8}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1391. <Class name="AZStd::unordered_map" field="m_patch" type="{18456A80-63CC-40C5-BF16-6AF94F9A9ECC}">
  1392. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1393. <Class name="AddressType" field="value1" value="F01997AC00000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1394. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF60000004A0000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1395. </Class>
  1396. </Class>
  1397. </Class>
  1398. </ObjectStream>
  1399. )";
  1400. // Load the patch from XML
  1401. // This triggers the Legacy DataPatch converter
  1402. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1403. // Apply provides the remaining class data to complete the conversion
  1404. DataPatch patch;
  1405. LoadPatchFromXML(legacyPatchXML, patch);
  1406. // Apply the patch and complete conversion
  1407. ObjectWithPointer sourceObj;
  1408. AZStd::unique_ptr<ObjectWithPointer> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1409. // Verify the patch applied as expected
  1410. EXPECT_TRUE(generatedObj);
  1411. EXPECT_TRUE(generatedObj->m_pointerInt);
  1412. EXPECT_EQ(*generatedObj->m_pointerInt, 74);
  1413. azdestroy(generatedObj->m_pointerInt);
  1414. }
  1415. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidContainerOverride_ApplySucceeds_FT)
  1416. {
  1417. // A Legacy DataPatch containing a vector of 5 objects with incrementing values and persistent Ids
  1418. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1419. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1420. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1421. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1422. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1423. <Class name="AddressType" field="value1" value="8C2AFF02000000000E00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1424. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CC00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000E000000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1425. </Class>
  1426. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1427. <Class name="AddressType" field="value1" value="8C2AFF02000000000D00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1428. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CB00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000D000000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1429. </Class>
  1430. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1431. <Class name="AddressType" field="value1" value="8C2AFF02000000000C00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1432. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CA00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000C000000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1433. </Class>
  1434. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1435. <Class name="AddressType" field="value1" value="8C2AFF02000000000B00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1436. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000C900795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000B000000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1437. </Class>
  1438. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1439. <Class name="AddressType" field="value1" value="8C2AFF02000000000A00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1440. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000C800795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000A000000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1441. </Class>
  1442. </Class>
  1443. </Class>
  1444. </ObjectStream>
  1445. )";
  1446. // Load the patch from XML
  1447. // This triggers the Legacy DataPatch converter
  1448. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1449. // Apply provides the remaining class data to complete the conversion
  1450. DataPatch patch;
  1451. LoadPatchFromXML(legacyPatchXML, patch);
  1452. // Apply the Patch and complete conversion
  1453. ObjectToPatch sourceObj;
  1454. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1455. constexpr int expectedSize = 5;
  1456. constexpr int persistentIdOffset = 10;
  1457. constexpr int dataOffset = 200;
  1458. // Verify the patch applied as expected for each value in the patched array
  1459. EXPECT_TRUE(generatedObj);
  1460. EXPECT_EQ(generatedObj->m_objectArray.size(), expectedSize);
  1461. for (int arrayIndex = 0; arrayIndex < expectedSize; ++arrayIndex)
  1462. {
  1463. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_persistentId, arrayIndex + persistentIdOffset);
  1464. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_data, arrayIndex + dataOffset);
  1465. }
  1466. }
  1467. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidContainerOverride_LegacyPatchUsesObjectStreamVersion1_ApplySucceeds_FT)
  1468. {
  1469. // A Legacy DataPatch containing a vector of 5 objects with incrementing values and persistent Ids
  1470. // Using ObjectStream V1 types for the unordered map, pair, and bytestream.
  1471. // Note: Does not use legacy types in the patch themselves (EX: a patched AZStd::string will use it's V3 typeId not V1)
  1472. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="1">
  1473. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1474. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1475. <Class name="AZStd::unordered_map" field="m_patch" type="{18456A80-63CC-40C5-BF16-6AF94F9A9ECC}">
  1476. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1477. <Class name="AddressType" field="value1" value="8C2AFF02000000000E00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1478. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CC00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000E000000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1479. </Class>
  1480. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1481. <Class name="AddressType" field="value1" value="8C2AFF02000000000D00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1482. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CB00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000D000000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1483. </Class>
  1484. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1485. <Class name="AddressType" field="value1" value="8C2AFF02000000000C00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1486. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000CA00795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000C000000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1487. </Class>
  1488. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1489. <Class name="AddressType" field="value1" value="8C2AFF02000000000B00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1490. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000C900795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000B000000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1491. </Class>
  1492. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1493. <Class name="AddressType" field="value1" value="8C2AFF02000000000A00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1494. <Class name="ByteStream" field="value2" value="000000000308D0C4D19C7EFF4F93A5F095F33FC855AA5C335CC94272039442EB384D42A1ADCB68F7E0EEF6000000C800795F998615D659793347CD4FC8B91163F3E2B0993A08000000000000000A000000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1495. </Class>
  1496. </Class>
  1497. </Class>
  1498. </ObjectStream>
  1499. )";
  1500. // Load the patch from XML
  1501. // This triggers the Legacy DataPatch converter
  1502. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1503. // Apply provides the remaining class data to complete the conversion
  1504. DataPatch patch;
  1505. LoadPatchFromXML(legacyPatchXML, patch);
  1506. // Apply the patch and complete conversion
  1507. ObjectToPatch sourceObj;
  1508. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1509. constexpr int expectedSize = 5;
  1510. constexpr int persistentIdOffset = 10;
  1511. constexpr int dataOffset = 200;
  1512. // Verify the patch applied as expected for each value in the patched array
  1513. EXPECT_TRUE(generatedObj);
  1514. EXPECT_EQ(generatedObj->m_objectArray.size(), expectedSize);
  1515. for (int arrayIndex = 0; arrayIndex < expectedSize; ++arrayIndex)
  1516. {
  1517. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_persistentId, arrayIndex + persistentIdOffset);
  1518. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_data, arrayIndex + dataOffset);
  1519. }
  1520. }
  1521. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidGenericTypeOverride_ApplySucceeds_FT)
  1522. {
  1523. // A Legacy DataPatch containing a string set to "Hello World"
  1524. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1525. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1526. <Class name="AZ::Uuid" field="m_targetClassId" value="{DE1EE15F-3458-40AE-A206-C6C957E2432B}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1527. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1528. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1529. <Class name="AddressType" field="value1" value="57E02DD400000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1530. <Class name="ByteStream" field="value2" value="00000000033903AAAB3F5C475A669EBCD5FA4DB353C90B48656C6C6F20576F726C640000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1531. </Class>
  1532. </Class>
  1533. </Class>
  1534. </ObjectStream>
  1535. )";
  1536. // Load the patch from XML
  1537. // This triggers the Legacy DataPatch converter
  1538. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1539. // Apply provides the remaining class data to complete the conversion
  1540. DataPatch patch;
  1541. LoadPatchFromXML(legacyPatchXML, patch);
  1542. // Apply the patch and complete conversion
  1543. ObjectsWithGenerics sourceObj;
  1544. AZStd::unique_ptr<ObjectsWithGenerics> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1545. constexpr const char* expectedString = "Hello World";
  1546. // Verify the patch applied as expected
  1547. EXPECT_TRUE(generatedObj);
  1548. EXPECT_STREQ(generatedObj->m_string.c_str(), expectedString);
  1549. }
  1550. TEST_F(PatchingTest, Apply_LegacyDataPatchWithValidGenericTypeOverride_LegacyPatchUsesObjectStreamVersion1_ApplySucceeds_FT)
  1551. {
  1552. // A Legacy DataPatch containing a string set to "Hello World" and using ObjectStream V1 types for the unordered map, pair, and bytestream.
  1553. // Note: Does not use legacy types in the patch themselves (EX: a patched AZStd::string will use it's V3 typeId not V1)
  1554. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="1">
  1555. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1556. <Class name="AZ::Uuid" field="m_targetClassId" value="{DE1EE15F-3458-40AE-A206-C6C957E2432B}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1557. <Class name="AZStd::unordered_map" field="m_patch" type="{18456A80-63CC-40C5-BF16-6AF94F9A9ECC}">
  1558. <Class name="AZStd::pair" field="element" type="{9F3F5302-3390-407A-A6F7-2E011E3BB686}">
  1559. <Class name="AddressType" field="value1" value="57E02DD400000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1560. <Class name="ByteStream" field="value2" value="00000000033903AAAB3F5C475A669EBCD5FA4DB353C90B48656C6C6F20576F726C640000" type="{6F949CC5-24A4-4229-AC8B-C5E6C70E145E}"/>
  1561. </Class>
  1562. </Class>
  1563. </Class>
  1564. </ObjectStream>
  1565. )";
  1566. // Load the patch from XML
  1567. // This triggers the Legacy DataPatch converter
  1568. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1569. // Apply provides the remaining class data to complete the conversion
  1570. DataPatch patch;
  1571. LoadPatchFromXML(legacyPatchXML, patch);
  1572. // Apply the patch and complete conversion
  1573. ObjectsWithGenerics sourceObj;
  1574. AZStd::unique_ptr<ObjectsWithGenerics> generatedObj(patch.Apply(&sourceObj, m_serializeContext.get()));
  1575. const char* expectedString = "Hello World";
  1576. // Verify the patch applied as expected
  1577. EXPECT_TRUE(generatedObj);
  1578. EXPECT_STREQ(generatedObj->m_string.c_str(), expectedString);
  1579. }
  1580. TEST_F(PatchingTest, Apply_LegacyDataPatchAppliedTwice_OnSecondApplyPatchHasBeenConverted_BothPatchAppliesSucceed_FT)
  1581. {
  1582. // A dataPatch containing an int set to 22
  1583. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1584. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1585. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1586. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1587. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1588. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1589. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000160000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1590. </Class>
  1591. </Class>
  1592. </Class>
  1593. </ObjectStream>
  1594. )";
  1595. constexpr int expectedValue = 22;
  1596. // Load the patch from stream
  1597. DataPatch patch;
  1598. LoadPatchFromXML(legacyPatchXML, patch);
  1599. // Apply the patch
  1600. ObjectToPatch sourceObj;
  1601. AZStd::unique_ptr<ObjectToPatch> generatedObjFirstApply(patch.Apply(&sourceObj, m_serializeContext.get()));
  1602. // Verify patch applied as expected
  1603. EXPECT_TRUE(generatedObjFirstApply);
  1604. EXPECT_EQ(generatedObjFirstApply->m_intValue, expectedValue);
  1605. // Apply the patch again
  1606. AZStd::unique_ptr<ObjectToPatch> generatedObjSecondApply(patch.Apply(&sourceObj, m_serializeContext.get()));
  1607. // Verify patch applied successfully the second time
  1608. EXPECT_TRUE(generatedObjSecondApply);
  1609. EXPECT_EQ(generatedObjSecondApply->m_intValue, expectedValue);
  1610. }
  1611. TEST_F(PatchingTest, Apply_PatchWrittenToThenReadFromStreamBeforeApply_PatchApplySucceeds_FT)
  1612. {
  1613. ObjectToPatch source;
  1614. ObjectToPatch target;
  1615. constexpr int targetArraySize = 999;
  1616. constexpr int targetValueScalar = 2;
  1617. constexpr int persistentIdOffset = 100;
  1618. // Build target array
  1619. target.m_objectArray.resize(targetArraySize);
  1620. for (size_t arrayIndex = 0; arrayIndex < target.m_objectArray.size(); ++arrayIndex)
  1621. {
  1622. target.m_objectArray[arrayIndex].m_data = static_cast<int>(arrayIndex * targetValueScalar);
  1623. target.m_objectArray[arrayIndex].m_persistentId = static_cast<int>((arrayIndex * targetValueScalar) + persistentIdOffset);
  1624. }
  1625. // Create patch in memory
  1626. DataPatch patch;
  1627. patch.Create(&source, &target, AZ::DataPatch::FlagsMap(), AZ::DataPatch::FlagsMap(), m_serializeContext.get());
  1628. // Serialize patch into stream
  1629. AZStd::vector<AZ::u8> streamBuffer;
  1630. WritePatchToByteStream(patch, streamBuffer);
  1631. // Load patch from stream
  1632. DataPatch loadedPatch;
  1633. LoadPatchFromByteStream(streamBuffer, loadedPatch);
  1634. // Verify integrity of loaded patch
  1635. EXPECT_TRUE(loadedPatch.IsValid() && loadedPatch.IsData());
  1636. // Apply the patch
  1637. AZStd::unique_ptr<ObjectToPatch> generatedObj(loadedPatch.Apply(&source, m_serializeContext.get()));
  1638. // Verify patch applied as expected
  1639. EXPECT_TRUE(generatedObj);
  1640. EXPECT_EQ(generatedObj->m_objectArray.size(), targetArraySize);
  1641. for (int arrayIndex = 0; arrayIndex < targetArraySize; ++arrayIndex)
  1642. {
  1643. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_data, arrayIndex * targetValueScalar);
  1644. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_persistentId, (arrayIndex * targetValueScalar) + persistentIdOffset);
  1645. }
  1646. }
  1647. TEST_F(PatchingTest, Apply_LegacyPatchWrittenToThenReadFromStreamBeforeApply_PatchApplySucceeds_FT)
  1648. {
  1649. // A Legacy DataPatch containing an int set to 57
  1650. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1651. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1652. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1653. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1654. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1655. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1656. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000390000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1657. </Class>
  1658. </Class>
  1659. </Class>
  1660. </ObjectStream>
  1661. )";
  1662. // Load the patch from stream
  1663. // Loading the legacy patch will run the converter
  1664. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1665. // Apply provides the remaining class data to complete the conversion
  1666. DataPatch patch;
  1667. LoadPatchFromXML(legacyPatchXML, patch);
  1668. // Serialize partially converted patch to stream
  1669. AZStd::vector<AZ::u8> streamBuffer;
  1670. WritePatchToByteStream(patch, streamBuffer);
  1671. // Load partially converted patch from stream
  1672. DataPatch loadedPatch;
  1673. LoadPatchFromByteStream(streamBuffer, loadedPatch);
  1674. // Verify integrity of loaded patch
  1675. EXPECT_TRUE(loadedPatch.IsValid() && loadedPatch.IsData());
  1676. // Apply the patch
  1677. ObjectToPatch source;
  1678. AZStd::unique_ptr<ObjectToPatch> generatedObj(loadedPatch.Apply(&source, m_serializeContext.get()));
  1679. // Verify the patch applied as expected
  1680. EXPECT_TRUE(generatedObj);
  1681. EXPECT_EQ(generatedObj->m_intValue, 57);
  1682. }
  1683. TEST_F(PatchingTest, Apply_LegacyPatchAppliedTwice_AppliedAndWrittenToStream_LoadedFromStreamAndApplied_PatchApplySucceeds_FT)
  1684. {
  1685. // A Legacy DataPatch containing an int set to 92
  1686. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1687. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1688. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1689. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1690. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1691. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1692. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF60000005C0000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1693. </Class>
  1694. </Class>
  1695. </Class>
  1696. </ObjectStream>
  1697. )";
  1698. constexpr int expectedValue = 92;
  1699. // Load the patch from stream
  1700. // Loading the legacy patch will run the converter
  1701. // Patch Data will be wrapped in the StreamWrapper type until Apply is called
  1702. // Apply provides the remaining class data to complete the conversion
  1703. DataPatch patch;
  1704. LoadPatchFromXML(legacyPatchXML, patch);
  1705. // Apply provides the remaining class data to complete the conversion
  1706. ObjectToPatch source;
  1707. AZStd::unique_ptr<ObjectToPatch> generatedObjFirstApply(patch.Apply(&source, m_serializeContext.get()));
  1708. EXPECT_TRUE(generatedObjFirstApply);
  1709. EXPECT_EQ(generatedObjFirstApply->m_intValue, expectedValue);
  1710. // Serialize fully converted patch to stream
  1711. AZStd::vector<AZ::u8> streamBuffer;
  1712. WritePatchToByteStream(patch, streamBuffer);
  1713. // Load fully converted patch from stream
  1714. DataPatch loadedPatch;
  1715. LoadPatchFromByteStream(streamBuffer, loadedPatch);
  1716. // Verify integrity of loaded patch
  1717. EXPECT_TRUE(loadedPatch.IsValid() && loadedPatch.IsData());
  1718. // Apply the patch
  1719. AZStd::unique_ptr<ObjectToPatch> generatedObjSecondApply(loadedPatch.Apply(&source, m_serializeContext.get()));
  1720. // Verify the patch applied as expected
  1721. EXPECT_TRUE(generatedObjSecondApply);
  1722. EXPECT_EQ(generatedObjSecondApply->m_intValue, expectedValue);
  1723. }
  1724. TEST_F(PatchingTest, LegacyDataPatchConverter_LegacyPatchXMLMissingTargetClassId_ConverterThrowsError_FT)
  1725. {
  1726. // A Legacy DataPatch containing an int set to 178 but missing its TargetClassId
  1727. // This should fail conversion
  1728. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1729. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1730. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1731. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1732. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1733. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000B20000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1734. </Class>
  1735. </Class>
  1736. </Class>
  1737. </ObjectStream>
  1738. )";
  1739. DataPatch patch;
  1740. // Load the patch from XML
  1741. // This triggers the Legacy DataPatch converter
  1742. // Verify the expected number of Errors/Asserts occur
  1743. // Expected errors: Failed to get data from m_targetClassId field during conversion, found in LegacyDataPatchConverter (DataPatch.cpp)
  1744. // Converter failed error found in ObjectStreamImpl::LoadClass (ObjectStream.cpp)
  1745. AZ_TEST_START_ASSERTTEST;
  1746. LoadPatchFromXML(legacyPatchXML, patch);
  1747. AZ_TEST_STOP_ASSERTTEST(2);
  1748. }
  1749. TEST_F(PatchingTest, LegacyDataPatchConverter_LegacyPatchXMLMissingAddressType_ConverterThrowsError_FT)
  1750. {
  1751. // A Legacy DataPatch containing an int set to 154 but missing its AddressType
  1752. // This should fail conversion
  1753. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1754. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1755. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1756. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1757. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1758. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF60000009A0000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1759. </Class>
  1760. </Class>
  1761. </Class>
  1762. </ObjectStream>
  1763. )";
  1764. DataPatch patch;
  1765. // Load the patch from XML
  1766. // This triggers the Legacy DataPatch Converter
  1767. // Verify the expected number of Errors/Asserts occur on conversion
  1768. // Expected errors: Failed to find both first and second values in pair during conversion, found in ConvertByteStreamMapToAnyMap (DataPatch.cpp)
  1769. // Converter failed error found in ObjectStreamImpl::LoadClass (ObjectStream.cpp)
  1770. AZ_TEST_START_ASSERTTEST;
  1771. LoadPatchFromXML(legacyPatchXML, patch);
  1772. AZ_TEST_STOP_ASSERTTEST(2);
  1773. }
  1774. TEST_F(PatchingTest, LegacyDataPatchConverter_LegacyPatchXMLMissingByteStream_ConverterThrowsError_FT)
  1775. {
  1776. // A Legacy DataPatch missing its ByteStream data and is expected to fail conversion
  1777. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1778. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1779. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1780. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1781. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1782. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1783. </Class>
  1784. </Class>
  1785. </Class>
  1786. </ObjectStream>
  1787. )";
  1788. DataPatch patch;
  1789. // Load the patch from XML
  1790. // This triggers the Legacy DataPatch Converter
  1791. // Verify the expected nummber of Errors/Asserts occur on conversion
  1792. // Expected errors: Failed to find both first and second values in pair during conversion, found in ConvertByteStreamMapToAnyMap (DataPatch.cpp)
  1793. // Converter failed error found in ObjectStreamImpl::LoadClass (ObjectStream.cpp)
  1794. AZ_TEST_START_ASSERTTEST;
  1795. LoadPatchFromXML(legacyPatchXML, patch);
  1796. AZ_TEST_STOP_ASSERTTEST(2);
  1797. }
  1798. TEST_F(PatchingTest, LegacyDataPatchConverter_LegacyPatchXMLMissingAddressTypeAndByteStream_ConverterThrowsError_FT)
  1799. {
  1800. // A Legacy DataPatch missing both its AddressType and ByteStream and is expected to fail conversion
  1801. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1802. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1803. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1804. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1805. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1806. </Class>
  1807. </Class>
  1808. </Class>
  1809. </ObjectStream>
  1810. )";
  1811. DataPatch patch;
  1812. // Load the patch from XML
  1813. // This triggers the Legacy DataPatch Converter
  1814. // Verify the expected number of Errors/Asserts occur
  1815. // Expected errors: Failed to find both first and second values in pair during conversion, found in ConvertByteStreamMapToAnyMap (DataPatch.cpp)
  1816. // Converter failed error found in ObjectStreamImpl::LoadClass (ObjectStream.cpp)
  1817. AZ_TEST_START_ASSERTTEST;
  1818. LoadPatchFromXML(legacyPatchXML, patch);
  1819. AZ_TEST_STOP_ASSERTTEST(2);
  1820. }
  1821. TEST_F(PatchingTest, Apply_LegacyPatchXMLHasInvalidByteStream_ConverterThrowsError_FT)
  1822. {
  1823. // A Legacy DataPatch expecting to hold an int but containing an invalid bytestream and is expected to fail conversion
  1824. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1825. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1826. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1827. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1828. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1829. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1830. <Class name="ByteStream" field="value2" value="00FFFFFFFF" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1831. </Class>
  1832. </Class>
  1833. </Class>
  1834. </ObjectStream>
  1835. )";
  1836. // Load the patch from XML
  1837. // This triggers the Legacy DataPatch converter
  1838. // The invalid bytestream will be stored in a StreamWrapper until Apply
  1839. DataPatch patch;
  1840. LoadPatchFromXML(legacyPatchXML, patch);
  1841. ObjectToPatch source;
  1842. // Apply the patch and complete conversion
  1843. // The stored StreamWrapper will attempt to load and fail
  1844. // Verify the expected number of Errors/Asserts occur
  1845. // Expected errors: Stream is a newer version than object stream supports, found in ObjectStreamImpl::Start (ObjectStream.cpp)
  1846. // Failed to load StreamWrapper during DataPatch Apply, found in DataNodeTree::ApplyToElements (DataPatch.cpp)
  1847. AZ_TEST_START_ASSERTTEST;
  1848. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1849. AZ_TEST_STOP_ASSERTTEST(2);
  1850. }
  1851. TEST_F(PatchingTest, Apply_LegacyPatchXMLHasIncorrectAddressType_ApplyFails_FT)
  1852. {
  1853. // A Legacy DataPatch with an int set to 39 but with an incorrect AddressType
  1854. // AddressType to location for an int replaced with AddressType for value in an array structure
  1855. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1856. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1857. <Class name="AZ::Uuid" field="m_targetClassId" value="{47E5CF10-3FA1-4064-BE7A-70E3143B4025}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1858. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1859. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1860. <Class name="AddressType" field="value1" value="8C2AFF02000000000A00000000000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1861. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000270000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1862. </Class>
  1863. </Class>
  1864. </Class>
  1865. </ObjectStream>
  1866. )";
  1867. // Load the patch from XML
  1868. // This triggers the Legacy DataPatch converter
  1869. // The incorrect AddressType will be stored to direct patching for the valid ByteStream
  1870. DataPatch patch;
  1871. LoadPatchFromXML(legacyPatchXML, patch);
  1872. ObjectToPatch source;
  1873. source.m_intValue = 0;
  1874. // Apply the patch, conversion will not complete during this stage
  1875. // Since AddressType is invalid, the underlying data will not be requested during apply and will not be fully converted
  1876. AZ_TEST_START_TRACE_SUPPRESSION;
  1877. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1878. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  1879. // We expect the value 39 to not be patched during apply and m_intValue to remain at 0
  1880. EXPECT_TRUE(generatedObj);
  1881. EXPECT_EQ(generatedObj->m_intValue, 0);
  1882. }
  1883. TEST_F(PatchingTest, Apply_LegacyPatchXMLHasIncorrectTargetClassId_ApplyFailsAndReturnsNull_FT)
  1884. {
  1885. // A Legacy DataPatch containing an int set to 203
  1886. // TargetClassId set to DataPatch type Id which is incorrect for the type being contained
  1887. AZStd::string_view legacyPatchXML = R"(<ObjectStream version="3">
  1888. <Class name="DataPatch" type="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}">
  1889. <Class name="AZ::Uuid" field="m_targetClassId" value="{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
  1890. <Class name="AZStd::unordered_map" field="m_patch" type="{CF3B3C65-49C0-5199-B671-E75347EB25C2}">
  1891. <Class name="AZStd::pair" field="element" type="{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}">
  1892. <Class name="AddressType" field="value1" value="C705B33500000000" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
  1893. <Class name="ByteStream" field="value2" value="00000000031C72039442EB384D42A1ADCB68F7E0EEF6000000CB0000" type="{ADFD596B-7177-5519-9752-BC418FE42963}"/>
  1894. </Class>
  1895. </Class>
  1896. </Class>
  1897. </ObjectStream>
  1898. )";
  1899. // Load the patch from XML
  1900. // This triggers the Legacy DataPatch converter
  1901. DataPatch patch;
  1902. LoadPatchFromXML(legacyPatchXML, patch);
  1903. // Apply the patch, conversion will not complete during this stage
  1904. // Since targetClassId does not match supplied source type Apply is expected to return nullptr
  1905. ObjectToPatch source;
  1906. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1907. // Verify Apply returned a nullptr
  1908. EXPECT_FALSE(generatedObj);
  1909. }
  1910. TEST_F(PatchingTest, AddressTypeSerializerLoad_AddressTypeIsValid_AddressHasOnlyClassData_LoadSucceeds_FT)
  1911. {
  1912. const int expectedValue = 52;
  1913. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntXML(nullptr, expectedValue);
  1914. DataPatch patch;
  1915. ObjectToPatch source;
  1916. // Verify address deserializes with no errors
  1917. AZ_TEST_START_TRACE_SUPPRESSION;
  1918. LoadPatchFromXML(patchXML, patch);
  1919. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  1920. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1921. // Verify addressed field m_int was patched correctly (verifies integrity of address)
  1922. EXPECT_TRUE(generatedObj);
  1923. EXPECT_EQ(generatedObj->m_intValue, expectedValue);
  1924. }
  1925. TEST_F(PatchingTest, AddressTypeSerializerLoad_AddressTypeIsValid_AddressHasClassAndIndexData_LoadSucceeds_FT)
  1926. {
  1927. const size_t expectedContainerSize = 5;
  1928. const size_t persistentIdOffset = 10;
  1929. const size_t dataOffset = 0;
  1930. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntVectorXML(nullptr, dataOffset, persistentIdOffset);
  1931. DataPatch patch;
  1932. ObjectToPatch source;
  1933. // Verify address deserializes with no errors
  1934. AZ_TEST_START_ASSERTTEST;
  1935. LoadPatchFromXML(patchXML, patch);
  1936. AZ_TEST_STOP_ASSERTTEST(0);
  1937. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1938. // Verify integrity of patched object
  1939. EXPECT_TRUE(generatedObj);
  1940. EXPECT_EQ(generatedObj->m_objectArray.size(), expectedContainerSize);
  1941. for (size_t arrayIndex = 0; arrayIndex < expectedContainerSize; ++arrayIndex)
  1942. {
  1943. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_data, arrayIndex);
  1944. EXPECT_EQ(generatedObj->m_objectArray[arrayIndex].m_persistentId, arrayIndex + persistentIdOffset);
  1945. }
  1946. }
  1947. TEST_F(PatchingTest, AddressTypeSerializerLoad_AddressTypeIsInvalid_InvalidElementsInPath_LoadFails_FT)
  1948. {
  1949. AZStd::string pathWithInvalidElements = GetValidAddressForXMLDataPatchV1AddressTypeIntXML();
  1950. pathWithInvalidElements += "not/a/valid/path";
  1951. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntXML(pathWithInvalidElements.c_str(), 0);
  1952. DataPatch patch;
  1953. // Load the patch from XML
  1954. // This triggers AddressTypeSerializer::Load
  1955. // Expected error: AddressType failed to load due to invalid element in path
  1956. AZ_TEST_START_ASSERTTEST;
  1957. LoadPatchFromXML(patchXML, patch);
  1958. AZ_TEST_STOP_ASSERTTEST(2);
  1959. }
  1960. TEST_F(PatchingTest, AddressTypeSerializerLoad_AddressTypeIsInvalid_MissingPathDelimiter_LoadFails_FT)
  1961. {
  1962. // Build a path from a valid path minus the trailing "/" delimiter
  1963. AZStd::string validPath = GetValidAddressForXMLDataPatchV1AddressTypeIntXML();
  1964. AZStd::string validPathMissingDelimiter(validPath.c_str(), strlen(validPath.c_str()) - 1);
  1965. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntXML(validPathMissingDelimiter.c_str(), 0);
  1966. DataPatch patch;
  1967. // Load the patch from XML
  1968. // This triggers AddressTypeSerializer::Load
  1969. // Expected error: AddressType failed to load due to path not containing valid delimiter "/"
  1970. AZ_TEST_START_ASSERTTEST;
  1971. LoadPatchFromXML(patchXML, patch);
  1972. AZ_TEST_STOP_ASSERTTEST(2);
  1973. }
  1974. TEST_F(PatchingTest, Apply_AddressTypeIsInvalid_SingleEntryInPatch_ApplyFails_FT)
  1975. {
  1976. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntXML("not/a/valid/path", 0);
  1977. DataPatch patch;
  1978. ObjectToPatch source;
  1979. // Load the patch from XML
  1980. // This triggers AddressTypeSerializer::Load
  1981. // Expected errors: AddressType failed to load due to invalid element in path (DataPatch.cpp)
  1982. // Apply fails due to patch containing Invalid address during Apply (DataPatch.cpp)
  1983. AZ_TEST_START_ASSERTTEST;
  1984. LoadPatchFromXML(patchXML, patch);
  1985. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  1986. AZ_TEST_STOP_ASSERTTEST(3);
  1987. }
  1988. TEST_F(PatchingTest, AddressTypeSerializerLoad_AddressTypeIsEmpty_SingleEntryInPatch_ApplySucceeds_FT)
  1989. {
  1990. // An empty address on a single entry patch denotes that the root element is being patched
  1991. // Validate that we succesfully load an emtpy address
  1992. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntXML("", 0);
  1993. DataPatch patch;
  1994. ObjectToPatch source;
  1995. // Verify address deserializes with no errors
  1996. AZ_TEST_START_ASSERTTEST;
  1997. LoadPatchFromXML(patchXML, patch);
  1998. AZ_TEST_STOP_ASSERTTEST(0);
  1999. }
  2000. TEST_F(PatchingTest, Apply_AddressTypeIsInvalid_MultipleEntriesInPatch_ApplyFails_FT)
  2001. {
  2002. AZStd::string patchXML = BuildXMLDataPatchV1AddressTypeIntVectorXML("not/a/valid/path", 0, 0);
  2003. DataPatch patch;
  2004. ObjectToPatch source;
  2005. // Load the patch from XML
  2006. // This triggers AddressTypeSerializer::Load
  2007. // Expected errors: AddressType failed to load due to invalid element in path (DataPatch.cpp)
  2008. // Apply fails due to empty AddressType (DataPatch.cpp)
  2009. AZ_TEST_START_ASSERTTEST;
  2010. LoadPatchFromXML(patchXML, patch);
  2011. AZStd::unique_ptr<ObjectToPatch> generatedObj(patch.Apply(&source, m_serializeContext.get()));
  2012. AZ_TEST_STOP_ASSERTTEST(3);
  2013. }
  2014. TEST_F(PatchingTest, AddressTypeElementLoad_PathElementHasValidPathForClassType_LoadIsSuccessful_FT)
  2015. {
  2016. const char* expectedTypeId = "{D0C4D19C-7EFF-4F93-A5F0-95F33FC855AA}";
  2017. const char* expectedAddressElement = "ClassA";
  2018. const int expectedVersion = 5000;
  2019. DataPatch::AddressTypeElement addressTypeElement =
  2020. m_addressTypeSerializer->LoadAddressElementFromPath(AZStd::string::format("somecharacters(%s)::%s%s%i/",
  2021. expectedTypeId,
  2022. expectedAddressElement,
  2023. V1AddressTypeElementVersionDelimiter,
  2024. expectedVersion));
  2025. EXPECT_TRUE(addressTypeElement.IsValid());
  2026. EXPECT_EQ(addressTypeElement.GetElementTypeId(), AZ::Uuid(expectedTypeId));
  2027. EXPECT_EQ(addressTypeElement.GetAddressElement(), AZ_CRC(expectedAddressElement));
  2028. EXPECT_EQ(addressTypeElement.GetElementVersion(), expectedVersion);
  2029. }
  2030. TEST_F(PatchingTest, AddressTypeElementLoad_PathElementHasValidPathForIndexType_LoadIsSuccessful_FT)
  2031. {
  2032. const char* expectedTypeId = "{07DEDB71-0585-5BE6-83FF-1C9029B9E5DB}";
  2033. const int expectedAddressElement = 4321;
  2034. const int expectedVersion = 2222;
  2035. DataPatch::AddressTypeElement addressTypeElement =
  2036. m_addressTypeSerializer->LoadAddressElementFromPath(AZStd::string::format("somecharacters(%s)#%i%s%i/",
  2037. expectedTypeId,
  2038. expectedAddressElement,
  2039. V1AddressTypeElementVersionDelimiter,
  2040. expectedVersion));
  2041. EXPECT_TRUE(addressTypeElement.IsValid());
  2042. EXPECT_EQ(addressTypeElement.GetAddressElement(), expectedAddressElement);
  2043. EXPECT_EQ(addressTypeElement.GetElementTypeId(), AZ::Uuid(expectedTypeId));
  2044. EXPECT_EQ(addressTypeElement.GetElementVersion(), expectedVersion);
  2045. }
  2046. TEST_F(PatchingTest, AddressTypeElementLoad_PathElementHasValidPathForNoneType_LoadIsSuccessful_FT)
  2047. {
  2048. const int expectedAddressElement = 9999;
  2049. DataPatch::AddressTypeElement addressTypeElement =
  2050. m_addressTypeSerializer->LoadAddressElementFromPath(AZStd::string::format("%i/", expectedAddressElement));
  2051. EXPECT_TRUE(addressTypeElement.IsValid());
  2052. EXPECT_EQ(addressTypeElement.GetAddressElement(), expectedAddressElement);
  2053. EXPECT_EQ(addressTypeElement.GetElementTypeId(), AZ::Uuid::CreateNull());
  2054. EXPECT_EQ(addressTypeElement.GetElementVersion(), std::numeric_limits<AZ::u32>::max());
  2055. }
  2056. TEST_F(PatchingTest, AddressTypeElementLoad_PathElementHasInvalidTypeId_LoadFails_FT)
  2057. {
  2058. AZStd::string pathElementWithInvalidTypeId = AZStd::string::format("somecharacters(invalidTypeId)::classB%s5678%s",
  2059. V1AddressTypeElementVersionDelimiter,
  2060. V1AddressTypeElementPathDelimiter);
  2061. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathElementWithInvalidTypeId);
  2062. EXPECT_FALSE(addressTypeElement.IsValid());
  2063. EXPECT_EQ(addressTypeElement.GetElementTypeId(), AZ::Uuid::CreateNull());
  2064. }
  2065. TEST_F(PatchingTest, AddressTypeElementLoad_TypeIdMissingParentheses_LoadFails_FT)
  2066. {
  2067. AZStd::string pathMissingParentheses = AZStd::string::format("somecharacters{3A8D5EC9-D70E-41CB-879C-DEF6A6D6ED03}::classE%s3000%s",
  2068. V1AddressTypeElementVersionDelimiter,
  2069. V1AddressTypeElementPathDelimiter);
  2070. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathMissingParentheses);
  2071. EXPECT_FALSE(addressTypeElement.IsValid());
  2072. }
  2073. TEST_F(PatchingTest, AddressTypeElementLoad_TypeIdMissingCurlyBraces_LoadFails_FT)
  2074. {
  2075. AZStd::string pathMissingCurlyBraces = AZStd::string::format("somecharacters(07DEDB71-0585-5BE6-83FF-1C9029B9E5DB)::classF%s9876%s",
  2076. V1AddressTypeElementVersionDelimiter,
  2077. V1AddressTypeElementPathDelimiter);
  2078. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathMissingCurlyBraces);
  2079. EXPECT_FALSE(addressTypeElement.IsValid());
  2080. }
  2081. TEST_F(PatchingTest, AddressTypeElementLoad_ClassTypePathElementMissingColons_LoadFails_FT)
  2082. {
  2083. AZStd::string pathElementMissingColons = AZStd::string::format("somecharacters({861A12B0-BD91-528E-9CEC-505246EE98DE})classC%s5432%s",
  2084. V1AddressTypeElementVersionDelimiter,
  2085. V1AddressTypeElementPathDelimiter);
  2086. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathElementMissingColons);
  2087. EXPECT_FALSE(addressTypeElement.IsValid());
  2088. }
  2089. TEST_F(PatchingTest, AddressTypeElementLoad_IndexTypePathElementMissingPound_LoadFails_FT)
  2090. {
  2091. AZStd::string pathElementMissingPound = AZStd::string::format("somecharacters({ADFD596B-7177-5519-9752-BC418FE42963})91011%s1122%s",
  2092. V1AddressTypeElementVersionDelimiter,
  2093. V1AddressTypeElementPathDelimiter);
  2094. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathElementMissingPound);
  2095. EXPECT_FALSE(addressTypeElement.IsValid());
  2096. }
  2097. TEST_F(PatchingTest, AddressTypeElementLoad_PathElementMissingDotBeforeVersion_LoadFails_FT)
  2098. {
  2099. AZStd::string pathMissingDot = AZStd::string::format("somecharacters({07DEDB71-0585-5BE6-83FF-1C9029B9E5DB})::classD4000%s",
  2100. V1AddressTypeElementPathDelimiter);
  2101. DataPatch::AddressTypeElement addressTypeElement = m_addressTypeSerializer->LoadAddressElementFromPath(pathMissingDot);
  2102. EXPECT_FALSE(addressTypeElement.IsValid());
  2103. }
  2104. TEST_F(PatchingTest, DataPatchFieldConverterForVersion1Patch_DoesNotRunVersion0To1Converter_Succeeds)
  2105. {
  2106. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2107. const ObjectWithNumericFieldV1 initialObject;
  2108. ObjectWithNumericFieldV1 testObject;
  2109. testObject.m_value = 3946393;
  2110. DataPatch testPatch;
  2111. testPatch.Create(&initialObject, &testObject, {}, {}, m_serializeContext.get());
  2112. // Unreflect ObjectWithNumericFieldV1 and reflect ObjectWithNumericFieldV2
  2113. {
  2114. m_serializeContext->EnableRemoveReflection();
  2115. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2116. m_serializeContext->DisableRemoveReflection();
  2117. ObjectWithNumericFieldV2::Reflect(m_serializeContext.get());
  2118. }
  2119. ObjectWithNumericFieldV2 initialObjectV2;
  2120. ObjectWithNumericFieldV2* patchedObject = testPatch.Apply(&initialObjectV2, m_serializeContext.get());
  2121. ASSERT_NE(nullptr, patchedObject);
  2122. EXPECT_DOUBLE_EQ(32.0, patchedObject->m_value);
  2123. // Clean up ObjectWithNumericFieldV2 patch data;
  2124. delete patchedObject;
  2125. // Unreflect remaining reflected classes
  2126. m_serializeContext->EnableRemoveReflection();
  2127. ObjectWithNumericFieldV2::Reflect(m_serializeContext.get());
  2128. m_serializeContext->DisableRemoveReflection();
  2129. }
  2130. TEST_F(PatchingTest, ObjectFieldConverter_CreateDataPatchInMemoryCanBeAppliedSuccessfully)
  2131. {
  2132. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2133. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2134. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2135. ObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2136. const ObjectFieldConverterV1 initialObject;
  2137. ObjectFieldConverterV1 testObject;
  2138. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2139. testObject.m_rootStringField = "Test1";
  2140. testObject.m_rootStringVector.emplace_back("Test2");
  2141. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2142. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2143. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2144. derivedInnerObject->m_stringField = "DerivedTest1";
  2145. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2146. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2147. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2148. DataPatch testPatch;
  2149. testPatch.Create(&initialObject, &testObject, {}, {}, m_serializeContext.get());
  2150. ObjectFieldConverterV1* patchedObject = testPatch.Apply(&initialObject, m_serializeContext.get());
  2151. EXPECT_EQ(testObject.m_rootStringField, patchedObject->m_rootStringField);
  2152. EXPECT_EQ(testObject.m_rootStringVector, patchedObject->m_rootStringVector);
  2153. InnerObjectFieldConverterV1& testInnerObject = testObject.m_rootInnerObject;
  2154. InnerObjectFieldConverterV1& patchedInnerObject = patchedObject->m_rootInnerObject;
  2155. EXPECT_EQ(testInnerObject.m_stringField, patchedInnerObject.m_stringField);
  2156. EXPECT_EQ(testInnerObject.m_stringVector, patchedInnerObject.m_stringVector);
  2157. auto patchedInnerObjectDerived = azrtti_cast<InnerObjectFieldConverterDerivedV1*>(patchedObject->m_baseInnerObjectPolymorphic);
  2158. ASSERT_NE(nullptr, patchedInnerObjectDerived);
  2159. EXPECT_EQ(derivedInnerObject->m_stringField, patchedInnerObjectDerived->m_stringField);
  2160. EXPECT_EQ(derivedInnerObject->m_stringVector, patchedInnerObjectDerived->m_stringVector);
  2161. EXPECT_EQ(derivedInnerObject->m_objectWithNumericField.m_value, patchedInnerObjectDerived->m_objectWithNumericField.m_value);
  2162. // Clean up original ObjectFieldConverterV1 object
  2163. delete testObject.m_baseInnerObjectPolymorphic;
  2164. // Clean up patched ObjectFieldConverterV1 object
  2165. delete patchedObject->m_baseInnerObjectPolymorphic;
  2166. delete patchedObject;
  2167. m_serializeContext->EnableRemoveReflection();
  2168. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2169. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2170. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2171. ObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2172. m_serializeContext->DisableRemoveReflection();
  2173. }
  2174. inline namespace NestedMemberFieldChangeConverter
  2175. {
  2176. class InnerObjectFieldConverterV2
  2177. {
  2178. public:
  2179. AZ_CLASS_ALLOCATOR(InnerObjectFieldConverterV2, SystemAllocator);
  2180. AZ_RTTI(InnerObjectFieldConverterV2, "{28E61B17-F321-4D4E-9F4C-00846C6631DE}");
  2181. virtual ~InnerObjectFieldConverterV2() = default;
  2182. static int64_t StringToInt64(const AZStd::string& value)
  2183. {
  2184. return static_cast<int64_t>(value.size());
  2185. }
  2186. static void Reflect(AZ::ReflectContext* reflectContext)
  2187. {
  2188. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2189. {
  2190. serializeContext->Class<InnerObjectFieldConverterV2>()
  2191. // A class version converter is needed to load an InnerObjectFieldConverterV2 when it is stored directly in patch.
  2192. // This occurs when patched element is a pointer to a class. In that case the entire class is serialized out
  2193. // Therefore when the DataPatch is loaded, the patch Data will load it's AZStd::any, which goes through the normal
  2194. // serialization flow, so if the class stored in the AZStd::any is an old version it needs to run through a Version Converter
  2195. ->Version(2, &VersionConverter)
  2196. ->Field("InnerBaseIntField", &InnerObjectFieldConverterV2::m_int64Field)
  2197. ->TypeChange("InnerBaseStringField", 1, 2, AZStd::function<int64_t(const AZStd::string&)>(&StringToInt64))
  2198. ->NameChange(1, 2, "InnerBaseStringField", "InnerBaseIntField")
  2199. ->Field("InnerBaseStringVector", &InnerObjectFieldConverterV2::m_stringVector)
  2200. ;
  2201. }
  2202. }
  2203. static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& rootElement)
  2204. {
  2205. if (rootElement.GetVersion() < 2)
  2206. {
  2207. AZStd::string stringField;
  2208. if (!rootElement.GetChildData(AZ_CRC_CE("InnerBaseStringField"), stringField))
  2209. {
  2210. AZ_Error("PatchingTest", false, "Unable to retrieve 'InnerBaseStringField' data for %u version of the InnerObjectFieldConverterClass",
  2211. rootElement.GetVersion());
  2212. return false;
  2213. }
  2214. rootElement.RemoveElementByName(AZ_CRC_CE("InnerBaseStringField"));
  2215. rootElement.AddElementWithData(context, "InnerBaseIntField", static_cast<int64_t>(stringField.size()));
  2216. }
  2217. return true;
  2218. }
  2219. int64_t m_int64Field;
  2220. AZStd::vector<AZStd::string> m_stringVector;
  2221. };
  2222. //! InnerObjectFieldConverterDerivedV2 is exactly the same as InnerObjectFieldConverterDerivedV1
  2223. //! It is just needed to state that InnerObjectFieldConverterV2 is a base class
  2224. using InnerObjectFieldConverterDerivedV1WithV2Base = InnerObjectFieldConverterDerivedV1Template<InnerObjectFieldConverterV2>;
  2225. //! ObjectFieldConverterV1WithMemberVersionChange is the same has the ObjectFieldConverterV1 class, it just substitutes out
  2226. //! the InnerObjectFieldConverterV1 with InnerObjectFieldConverterV2 that has the same typeid, but newer version
  2227. class ObjectFieldConverterV1WithMemberVersionChange
  2228. {
  2229. using ClassType = ObjectFieldConverterV1WithMemberVersionChange;
  2230. public:
  2231. AZ_CLASS_ALLOCATOR(ClassType, SystemAllocator);
  2232. AZ_TYPE_INFO(ClassType, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  2233. static void Reflect(AZ::ReflectContext* reflectContext)
  2234. {
  2235. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2236. {
  2237. serializeContext->Class<ClassType>()
  2238. ->Version(1)
  2239. ->Field("RootStringField", &ClassType::m_rootStringField)
  2240. ->Field("RootStringVector", &ClassType::m_rootStringVector)
  2241. ->Field("RootInnerObjectValue", &ClassType::m_rootInnerObject)
  2242. ->Field("RootInnerObjectPointer", &ClassType::m_baseInnerObjectPolymorphic)
  2243. ;
  2244. }
  2245. }
  2246. //! AZStd::string uses IDataSerializer for Serialization.
  2247. //! This is to test Field Converters for patched element that are directly on the patched class
  2248. AZStd::string m_rootStringField;
  2249. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2250. //! This is to test Field Converters for patched element that are directly on the patched class
  2251. AZStd::vector<AZStd::string> m_rootStringVector;
  2252. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2253. //! This is to test Field Converters for patched element that are directly on the patched class
  2254. InnerObjectFieldConverterV2 m_rootInnerObject{};
  2255. InnerObjectFieldConverterV2* m_baseInnerObjectPolymorphic{};
  2256. };
  2257. TEST_F(PatchingTest, ObjectFieldConverter_ChangeInnerFieldVersion_FieldConverterRunsSuccessfully)
  2258. {
  2259. using OriginalObjectField = ObjectFieldConverterV1;
  2260. using PatchedObjectField = ObjectFieldConverterV1WithMemberVersionChange;
  2261. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2262. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2263. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2264. OriginalObjectField::Reflect(m_serializeContext.get());
  2265. OriginalObjectField testObject;
  2266. // Change the defaults of the InnerObjectFieldConverterV2 member and InnerObjectFieldConverterV2 pointer member
  2267. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2268. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2269. derivedInnerObject->m_stringField = "DerivedTest1";
  2270. derivedInnerObject->m_objectWithNumericField.m_value = 10;
  2271. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2272. AZStd::vector<uint8_t> byteBuffer;
  2273. // Create DataPatch using ObjectFieldConverterV1
  2274. {
  2275. DataPatch testPatch;
  2276. const OriginalObjectField initialObjectV1;
  2277. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2278. // Write DataPatch to ByteStream before unreflected Version 1 of the InnerObjectFieldConverterV1 class
  2279. AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
  2280. WritePatchToByteStream(testPatch, byteBuffer);
  2281. }
  2282. // Now unreflect the ObjectFieldConverterV1, InnerObjectFieldConverterDerivedV1 and InnerObjectFieldConverterDerivedV1
  2283. // and reflect ObjectFieldConverterV1WithMemberVersionChange, InnerObjectFieldConverterV2 and InnerObjectFieldConverterDerivedV1WithV2Base
  2284. {
  2285. m_serializeContext->EnableRemoveReflection();
  2286. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2287. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2288. OriginalObjectField::Reflect(m_serializeContext.get());
  2289. m_serializeContext->DisableRemoveReflection();
  2290. InnerObjectFieldConverterV2::Reflect(m_serializeContext.get());
  2291. InnerObjectFieldConverterDerivedV1WithV2Base::Reflect(m_serializeContext.get());
  2292. PatchedObjectField::Reflect(m_serializeContext.get());
  2293. }
  2294. // Read DataPatch from ByteStream after reflecting Version 2 of the InnerObjectFieldConverterV2 class
  2295. // the reason this is required is the testPatch variable has as part of the patch data AZStd::any
  2296. // that wraps a instance of an InnerObjectFieldConverterDerivedV1 stored in an InnerObjectFieldConverterV1 pointer
  2297. // The virtual table points to the InnerObjectFieldConverterDerivedV1 class, which in a normal patching scenario
  2298. // The Data Patch would be loaded from disk after the new version of the InnerObjectFieldConverterV2 has reflected
  2299. // and therefore the patch instance data would be an object of InnerObjectFieldConverterDerivedV1WithV2Base with the
  2300. // correct vtable
  2301. AZ::DataPatch freshDataPatch;
  2302. LoadPatchFromByteStream(byteBuffer, freshDataPatch);
  2303. const PatchedObjectField initialObjectV2;
  2304. PatchedObjectField *patchedObjectV2 = freshDataPatch.Apply(&initialObjectV2, m_serializeContext.get());
  2305. ASSERT_NE(nullptr, patchedObjectV2);
  2306. EXPECT_EQ(10, patchedObjectV2->m_rootInnerObject.m_int64Field);
  2307. auto patchedDerivedInnerObject = azrtti_cast<InnerObjectFieldConverterDerivedV1WithV2Base*>(patchedObjectV2->m_baseInnerObjectPolymorphic);
  2308. ASSERT_NE(nullptr, patchedDerivedInnerObject);
  2309. EXPECT_EQ(12, patchedDerivedInnerObject->m_int64Field);
  2310. ASSERT_NE(nullptr, patchedDerivedInnerObject);
  2311. EXPECT_EQ(10, patchedDerivedInnerObject->m_objectWithNumericField.m_value);
  2312. // Clean up original ObjectFieldConverterV1 object
  2313. delete testObject.m_baseInnerObjectPolymorphic;
  2314. // Clean up patched ObjectFieldConverterV1 object
  2315. delete patchedObjectV2->m_baseInnerObjectPolymorphic;
  2316. delete patchedObjectV2;
  2317. m_serializeContext->EnableRemoveReflection();
  2318. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2319. InnerObjectFieldConverterV2::Reflect(m_serializeContext.get());
  2320. InnerObjectFieldConverterDerivedV1WithV2Base::Reflect(m_serializeContext.get());
  2321. PatchedObjectField::Reflect(m_serializeContext.get());
  2322. m_serializeContext->DisableRemoveReflection();
  2323. }
  2324. }
  2325. inline namespace RootLevelDataSerializerFieldConverter
  2326. {
  2327. // ObjectFieldConverterReplaceMemberDataSerializerV2, uses the same TypeId as ObjectFieldConverterV1
  2328. // for to test the FieldConverter for an IDataSerializer
  2329. class ObjectFieldConverterReplaceMemberDataSerializerV2
  2330. {
  2331. using ClassType = ObjectFieldConverterReplaceMemberDataSerializerV2;
  2332. public:
  2333. AZ_CLASS_ALLOCATOR(ObjectFieldConverterReplaceMemberDataSerializerV2, SystemAllocator);
  2334. AZ_TYPE_INFO(ObjectFieldConverterReplaceMemberDataSerializerV2, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  2335. static AZ::Uuid ConvertStringToUuid(const AZStd::string& value)
  2336. {
  2337. return AZ::Uuid::CreateName(value.data());
  2338. }
  2339. static void Reflect(AZ::ReflectContext* reflectContext)
  2340. {
  2341. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2342. {
  2343. serializeContext->Class<ClassType>()
  2344. ->Version(2)
  2345. ->Field("RootUuidField", &ClassType::m_rootUuidField)
  2346. ->NameChange(1, 2, "RootStringField", "RootUuidField")
  2347. // NOTE!! Type Change is prioritized before Name Change, so it works on the old Field name
  2348. ->TypeChange("RootStringField", 1, 2, AZStd::function<AZ::Uuid(const AZStd::string&)>(&ClassType::ConvertStringToUuid))
  2349. ->Field("RootStringVector", &ClassType::m_rootStringVector)
  2350. ->Field("RootInnerObjectValue", &ClassType::m_rootInnerObject)
  2351. ->Field("RootInnerObjectPointer", &ClassType::m_baseInnerObjectPolymorphic)
  2352. ;
  2353. }
  2354. }
  2355. //! AZStd::string uses IDataSerializer for Serialization.
  2356. //! This is to test Field Converters for patched element that are directly on the patched class
  2357. AZ::Uuid m_rootUuidField{ AZ::Uuid::CreateNull() };
  2358. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2359. //! This is to test Field Converters for patched element that are directly on the patched class
  2360. AZStd::vector<AZStd::string> m_rootStringVector;
  2361. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2362. //! This is to test Field Converters for patched element that are directly on the patched class
  2363. InnerObjectFieldConverterV1 m_rootInnerObject{};
  2364. InnerObjectFieldConverterV1* m_baseInnerObjectPolymorphic{};
  2365. };
  2366. // ObjectFieldConverterReplaceMemberDataSerializerV3, uses the same TypeId as ObjectFieldConverterV1
  2367. // for to test the FieldConverter that skips a level
  2368. class ObjectFieldConverterReplaceMemberDataSerializerV3
  2369. {
  2370. using ClassType = ObjectFieldConverterReplaceMemberDataSerializerV3;
  2371. public:
  2372. AZ_CLASS_ALLOCATOR(ObjectFieldConverterReplaceMemberDataSerializerV3, SystemAllocator);
  2373. AZ_TYPE_INFO(ObjectFieldConverterReplaceMemberDataSerializerV3, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  2374. static bool ConvertStringToBool(const AZStd::string& value)
  2375. {
  2376. return !value.empty();
  2377. }
  2378. static bool ConvertUuidToBool(const AZ::Uuid& value)
  2379. {
  2380. return !value.IsNull();
  2381. }
  2382. static void Reflect(AZ::ReflectContext* reflectContext)
  2383. {
  2384. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2385. {
  2386. serializeContext->Class<ClassType>()
  2387. ->Version(3)
  2388. ->Field("RootBoolField", &ClassType::m_rootBoolField)
  2389. ->NameChange(2, 3, "RootUuidField", "RootBoolField")
  2390. ->NameChange(1, 3, "RootStringField", "RootBoolField")
  2391. //! NOTE Type Change is prioritized before Name Change, so it works on the old Field name
  2392. ->TypeChange("RootUuidField", 2, 3, AZStd::function<bool(const AZ::Uuid&)>(&ClassType::ConvertUuidToBool))
  2393. ->TypeChange("RootStringField", 1, 3, AZStd::function<bool(const AZStd::string&)>(&ClassType::ConvertStringToBool))
  2394. ->Field("RootStringVector", &ClassType::m_rootStringVector)
  2395. ->Field("RootInnerObjectValue", &ClassType::m_rootInnerObject)
  2396. ->Field("RootInnerObjectPointer", &ClassType::m_baseInnerObjectPolymorphic)
  2397. ;
  2398. }
  2399. }
  2400. //! AZStd::string uses IDataSerializer for Serialization.
  2401. //! This is to test Field Converters for patched element that are directly on the patched class
  2402. bool m_rootBoolField{};
  2403. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2404. //! This is to test Field Converters for patched element that are directly on the patched class
  2405. AZStd::vector<AZStd::string> m_rootStringVector;
  2406. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2407. //! This is to test Field Converters for patched element that are directly on the patched class
  2408. InnerObjectFieldConverterV1 m_rootInnerObject{};
  2409. InnerObjectFieldConverterV1* m_baseInnerObjectPolymorphic{};
  2410. };
  2411. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataSerializerField_ConvertsFromV1ToV2_Successfully)
  2412. {
  2413. using OriginalObjectField = ObjectFieldConverterV1;
  2414. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataSerializerV2;
  2415. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2416. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2417. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2418. OriginalObjectField::Reflect(m_serializeContext.get());
  2419. OriginalObjectField testObject;
  2420. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2421. testObject.m_rootStringField = "Test1";
  2422. testObject.m_rootStringVector.emplace_back("Test2");
  2423. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2424. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2425. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2426. derivedInnerObject->m_stringField = "DerivedTest1";
  2427. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2428. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2429. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2430. // Create DataPatch using ObjectFieldConverterV1
  2431. DataPatch testPatch;
  2432. const OriginalObjectField initialObjectV1;
  2433. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2434. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2435. {
  2436. m_serializeContext->EnableRemoveReflection();
  2437. OriginalObjectField::Reflect(m_serializeContext.get());
  2438. m_serializeContext->DisableRemoveReflection();
  2439. PatchedObjectField::Reflect(m_serializeContext.get());
  2440. }
  2441. const PatchedObjectField initialObjectV2;
  2442. PatchedObjectField *patchedObjectV2 = testPatch.Apply(&initialObjectV2, m_serializeContext.get());
  2443. ASSERT_NE(nullptr, patchedObjectV2);
  2444. EXPECT_FALSE(patchedObjectV2->m_rootUuidField.IsNull());
  2445. // Clean up original ObjectFieldConverterV1 object
  2446. delete testObject.m_baseInnerObjectPolymorphic;
  2447. // Clean up patched ObjectFieldConverterV1 object
  2448. delete patchedObjectV2->m_baseInnerObjectPolymorphic;
  2449. delete patchedObjectV2;
  2450. m_serializeContext->EnableRemoveReflection();
  2451. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2452. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2453. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2454. PatchedObjectField::Reflect(m_serializeContext.get());
  2455. m_serializeContext->DisableRemoveReflection();
  2456. }
  2457. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataSerializerField_ConvertsFromV2ToV3_Successfully)
  2458. {
  2459. using OriginalObjectField = ObjectFieldConverterReplaceMemberDataSerializerV2;
  2460. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataSerializerV3;
  2461. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2462. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2463. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2464. OriginalObjectField::Reflect(m_serializeContext.get());
  2465. OriginalObjectField testObject;
  2466. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2467. testObject.m_rootUuidField = AZ::Uuid::CreateString("{10000000-0000-0000-0000-000000000000}");
  2468. testObject.m_rootStringVector.emplace_back("Test2");
  2469. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2470. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2471. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2472. derivedInnerObject->m_stringField = "DerivedTest1";
  2473. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2474. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2475. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2476. // Create DataPatch using ObjectFieldConverterV1
  2477. DataPatch testPatch;
  2478. const OriginalObjectField initialObjectV1;
  2479. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2480. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2481. {
  2482. m_serializeContext->EnableRemoveReflection();
  2483. OriginalObjectField::Reflect(m_serializeContext.get());
  2484. m_serializeContext->DisableRemoveReflection();
  2485. PatchedObjectField::Reflect(m_serializeContext.get());
  2486. }
  2487. const PatchedObjectField initialObjectV3;
  2488. PatchedObjectField *patchedObjectV3 = testPatch.Apply(&initialObjectV3, m_serializeContext.get());
  2489. ASSERT_NE(nullptr, patchedObjectV3);
  2490. EXPECT_TRUE(patchedObjectV3->m_rootBoolField);
  2491. // Clean up original ObjectFieldConverterV1 object
  2492. delete testObject.m_baseInnerObjectPolymorphic;
  2493. // Clean up patched ObjectFieldConverterV1 object
  2494. delete patchedObjectV3->m_baseInnerObjectPolymorphic;
  2495. delete patchedObjectV3;
  2496. m_serializeContext->EnableRemoveReflection();
  2497. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2498. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2499. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2500. PatchedObjectField::Reflect(m_serializeContext.get());
  2501. m_serializeContext->DisableRemoveReflection();
  2502. }
  2503. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataSerializerField_ConvertsFromV1ToV3_Successfully)
  2504. {
  2505. using OriginalObjectField = ObjectFieldConverterV1;
  2506. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataSerializerV3;
  2507. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2508. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2509. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2510. OriginalObjectField::Reflect(m_serializeContext.get());
  2511. OriginalObjectField testObject;
  2512. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2513. testObject.m_rootStringField = "StringV1";
  2514. testObject.m_rootStringVector.emplace_back("Test2");
  2515. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2516. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2517. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2518. derivedInnerObject->m_stringField = "DerivedTest1";
  2519. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2520. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2521. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2522. // Create DataPatch using ObjectFieldConverterV1
  2523. DataPatch testPatch;
  2524. const OriginalObjectField initialObjectV1;
  2525. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2526. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2527. {
  2528. m_serializeContext->EnableRemoveReflection();
  2529. OriginalObjectField::Reflect(m_serializeContext.get());
  2530. m_serializeContext->DisableRemoveReflection();
  2531. PatchedObjectField::Reflect(m_serializeContext.get());
  2532. }
  2533. const PatchedObjectField initialObjectV3;
  2534. PatchedObjectField *patchedObjectV3 = testPatch.Apply(&initialObjectV3, m_serializeContext.get());
  2535. ASSERT_NE(nullptr, patchedObjectV3);
  2536. EXPECT_TRUE(patchedObjectV3->m_rootBoolField);
  2537. // Clean up original ObjectFieldConverterV1 object
  2538. delete testObject.m_baseInnerObjectPolymorphic;
  2539. // Clean up patched ObjectFieldConverterV1 object
  2540. delete patchedObjectV3->m_baseInnerObjectPolymorphic;
  2541. delete patchedObjectV3;
  2542. m_serializeContext->EnableRemoveReflection();
  2543. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2544. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2545. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2546. PatchedObjectField::Reflect(m_serializeContext.get());
  2547. m_serializeContext->DisableRemoveReflection();
  2548. }
  2549. }
  2550. inline namespace RootLevelDataContainerFieldConverter
  2551. {
  2552. // ObjectFieldConverterReplaceMemberDataConverterV2, uses the same TypeId as ObjectFieldConverterV1
  2553. // for to test the FieldConverter for an IDataConverter
  2554. class ObjectFieldConverterReplaceMemberDataConverterV2
  2555. {
  2556. using ClassType = ObjectFieldConverterReplaceMemberDataConverterV2;
  2557. public:
  2558. AZ_CLASS_ALLOCATOR(ClassType, SystemAllocator);
  2559. AZ_TYPE_INFO(ClassType, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  2560. static AZStd::array<AZStd::string, 5> ConvertStringVectorToStringArray(const AZStd::vector<AZStd::string>& value)
  2561. {
  2562. AZStd::array<AZStd::string, 5> result;
  2563. size_t elementsToCopy = AZStd::min(result.size(), value.size());
  2564. for (size_t valueIndex = 0; valueIndex < elementsToCopy; ++valueIndex)
  2565. {
  2566. result[valueIndex] = value[valueIndex];
  2567. }
  2568. return result;
  2569. }
  2570. static void Reflect(AZ::ReflectContext* reflectContext)
  2571. {
  2572. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2573. {
  2574. // Because containers are implicitly reflected when used as part of a class reflection
  2575. // the ObjectFieldConverterV1 AZStd::vector<AZStd::string> class is not reflected
  2576. // if the ObjectFieldConverterV1 did not reflect
  2577. serializeContext->RegisterGenericType<AZStd::vector<AZStd::string>>();
  2578. serializeContext->Class<ClassType>()
  2579. ->Version(2)
  2580. ->Field("RootStringField", &ClassType::m_rootStringField)
  2581. ->Field("RootStringArray", &ClassType::m_rootStringArray)
  2582. ->TypeChange("RootStringVector", 1, 2, AZStd::function<AZStd::array<AZStd::string, 5>(const AZStd::vector<AZStd::string>&)>(&ConvertStringVectorToStringArray))
  2583. ->NameChange(1, 2, "RootStringVector", "RootStringArray")
  2584. ->Field("RootInnerObjectValue", &ClassType::m_rootInnerObject)
  2585. ->Field("RootInnerObjectPointer", &ClassType::m_baseInnerObjectPolymorphic)
  2586. ;
  2587. }
  2588. }
  2589. //! AZStd::string uses IDataSerializer for Serialization.
  2590. //! This is to test Field Converters for patched element that are directly on the patched class
  2591. AZStd::string m_rootStringField;
  2592. //! AZStd::array<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2593. //! This is to test Field Converters for patched element that are directly on the patched class
  2594. AZStd::array<AZStd::string, 5> m_rootStringArray;
  2595. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2596. //! This is to test Field Converters for patched element that are directly on the patched class
  2597. InnerObjectFieldConverterV1 m_rootInnerObject{};
  2598. InnerObjectFieldConverterV1* m_baseInnerObjectPolymorphic{};
  2599. };
  2600. // ObjectFieldConverterReplaceMemberDataConverterV3, uses the same TypeId as ObjectFieldConverterV1
  2601. // for to test the FieldConverter that skips a level
  2602. class ObjectFieldConverterReplaceMemberDataConverterV3
  2603. {
  2604. using ClassType = ObjectFieldConverterReplaceMemberDataConverterV3;
  2605. public:
  2606. AZ_CLASS_ALLOCATOR(ClassType, SystemAllocator);
  2607. AZ_TYPE_INFO(ClassType, "{5722C4E4-25DE-48C5-BC89-0EE9D38DF433}");
  2608. static AZStd::list<AZStd::string> ConvertStringVectorToStringList(const AZStd::vector<AZStd::string>& value)
  2609. {
  2610. AZStd::list<AZStd::string> result(value.begin(), value.end());
  2611. return result;
  2612. }
  2613. static AZStd::list<AZStd::string> ConvertStringArrayToStringList(const AZStd::array<AZStd::string, 5>& value)
  2614. {
  2615. AZStd::list<AZStd::string> result(value.begin(), value.end());
  2616. return result;
  2617. }
  2618. static void Reflect(AZ::ReflectContext* reflectContext)
  2619. {
  2620. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2621. {
  2622. serializeContext->Class<ClassType>()
  2623. ->Version(3)
  2624. ->Field("RootStringField", &ClassType::m_rootStringField)
  2625. ->Field("RootStringList", &ClassType::m_rootStringList)
  2626. // The TypeChange and NameChange converters are interleaved purposefully to make sure that the order of declaration
  2627. // of the converters doesn't affect the conversion result
  2628. ->TypeChange("RootStringVector", 1, 3, AZStd::function<AZStd::list<AZStd::string>(const AZStd::vector<AZStd::string>&)>(&ConvertStringVectorToStringList))
  2629. ->NameChange(1, 3, "RootStringVector", "RootStringList")
  2630. ->NameChange(2, 3, "RootStringArray", "RootStringList")
  2631. ->TypeChange("RootStringArray", 2, 3, AZStd::function<AZStd::list<AZStd::string>(const AZStd::array<AZStd::string, 5>&)>(&ConvertStringArrayToStringList))
  2632. ->Field("RootInnerObjectValue", &ClassType::m_rootInnerObject)
  2633. ->Field("RootInnerObjectPointer", &ClassType::m_baseInnerObjectPolymorphic)
  2634. ;
  2635. }
  2636. }
  2637. //! AZStd::string uses IDataSerializer for Serialization.
  2638. //! This is to test Field Converters for patched element that are directly on the patched class
  2639. AZStd::string m_rootStringField;
  2640. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2641. //! This is to test Field Converters for patched element that are directly on the patched class
  2642. AZStd::list<AZStd::string> m_rootStringList;
  2643. //! AZStd::vector<AZStd::string> uses IDataContainer for Serialization. The inner AZStd::string class uses IDataSerializer for serialization
  2644. //! This is to test Field Converters for patched element that are directly on the patched class
  2645. InnerObjectFieldConverterV1 m_rootInnerObject{};
  2646. InnerObjectFieldConverterV1* m_baseInnerObjectPolymorphic{};
  2647. };
  2648. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataContainerField_ConvertsFromV1ToV2_Successfully)
  2649. {
  2650. using OriginalObjectField = ObjectFieldConverterV1;
  2651. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataConverterV2;
  2652. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2653. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2654. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2655. OriginalObjectField::Reflect(m_serializeContext.get());
  2656. OriginalObjectField testObject;
  2657. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2658. testObject.m_rootStringField = "Test1";
  2659. testObject.m_rootStringVector.emplace_back("Test2");
  2660. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2661. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2662. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2663. derivedInnerObject->m_stringField = "DerivedTest1";
  2664. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2665. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2666. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2667. // Create DataPatch using ObjectFieldConverterV1
  2668. DataPatch testPatch;
  2669. const OriginalObjectField initialObjectV1;
  2670. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2671. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2672. {
  2673. m_serializeContext->EnableRemoveReflection();
  2674. OriginalObjectField::Reflect(m_serializeContext.get());
  2675. m_serializeContext->DisableRemoveReflection();
  2676. PatchedObjectField::Reflect(m_serializeContext.get());
  2677. }
  2678. const PatchedObjectField initialObjectV2;
  2679. PatchedObjectField *patchedObjectV2 = testPatch.Apply(&initialObjectV2, m_serializeContext.get());
  2680. ASSERT_NE(nullptr, patchedObjectV2);
  2681. EXPECT_EQ("Test2", patchedObjectV2->m_rootStringArray.front());
  2682. // Clean up original ObjectFieldConverterV1 object
  2683. delete testObject.m_baseInnerObjectPolymorphic;
  2684. // Clean up patched ObjectFieldConverterV1 object
  2685. delete patchedObjectV2->m_baseInnerObjectPolymorphic;
  2686. delete patchedObjectV2;
  2687. m_serializeContext->EnableRemoveReflection();
  2688. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2689. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2690. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2691. PatchedObjectField::Reflect(m_serializeContext.get());
  2692. m_serializeContext->DisableRemoveReflection();
  2693. }
  2694. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataContainerField_ConvertsFromV2ToV3_Successfully)
  2695. {
  2696. using OriginalObjectField = ObjectFieldConverterReplaceMemberDataConverterV2;
  2697. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataConverterV3;
  2698. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2699. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2700. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2701. OriginalObjectField::Reflect(m_serializeContext.get());
  2702. OriginalObjectField testObject;
  2703. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2704. testObject.m_rootStringField = "Test1";
  2705. testObject.m_rootStringArray[0] = "BigTest";
  2706. testObject.m_rootStringArray[3] = "SuperTest";
  2707. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2708. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2709. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2710. derivedInnerObject->m_stringField = "DerivedTest1";
  2711. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2712. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2713. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2714. // Create DataPatch using ObjectFieldConverterV1
  2715. DataPatch testPatch;
  2716. const OriginalObjectField initialObjectV1;
  2717. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2718. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2719. {
  2720. m_serializeContext->EnableRemoveReflection();
  2721. OriginalObjectField::Reflect(m_serializeContext.get());
  2722. m_serializeContext->DisableRemoveReflection();
  2723. PatchedObjectField::Reflect(m_serializeContext.get());
  2724. }
  2725. const PatchedObjectField initialObjectV3;
  2726. PatchedObjectField *patchedObjectV3 = testPatch.Apply(&initialObjectV3, m_serializeContext.get());
  2727. ASSERT_NE(nullptr, patchedObjectV3);
  2728. ASSERT_EQ(2, patchedObjectV3->m_rootStringList.size());
  2729. auto patchListIter = patchedObjectV3->m_rootStringList.begin();
  2730. EXPECT_EQ("BigTest", *patchListIter++);
  2731. EXPECT_EQ("SuperTest", *patchListIter++);
  2732. // Clean up original ObjectFieldConverterV1 object
  2733. delete testObject.m_baseInnerObjectPolymorphic;
  2734. // Clean up patched ObjectFieldConverterV1 object
  2735. delete patchedObjectV3->m_baseInnerObjectPolymorphic;
  2736. delete patchedObjectV3;
  2737. m_serializeContext->EnableRemoveReflection();
  2738. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2739. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2740. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2741. PatchedObjectField::Reflect(m_serializeContext.get());
  2742. m_serializeContext->DisableRemoveReflection();
  2743. }
  2744. TEST_F(PatchingTest, ObjectFieldConverter_ChangeRootDataConverterField_ConvertsFromV1ToV3_Successfully)
  2745. {
  2746. using OriginalObjectField = ObjectFieldConverterV1;
  2747. using PatchedObjectField = ObjectFieldConverterReplaceMemberDataConverterV3;
  2748. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2749. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2750. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2751. OriginalObjectField::Reflect(m_serializeContext.get());
  2752. OriginalObjectField testObject;
  2753. // Change the defaults of elements on the ObjectFieldConverterV1 object
  2754. testObject.m_rootStringField = "StringV1";
  2755. testObject.m_rootStringVector.emplace_back("Test2");
  2756. testObject.m_rootInnerObject.m_stringField = "InnerTest1";
  2757. testObject.m_rootInnerObject.m_stringVector.emplace_back("InnerTest2");
  2758. auto derivedInnerObject = aznew InnerObjectFieldConverterDerivedV1;
  2759. derivedInnerObject->m_stringField = "DerivedTest1";
  2760. derivedInnerObject->m_stringVector.emplace_back("DerivedTest2");
  2761. derivedInnerObject->m_objectWithNumericField.m_value = 1;
  2762. testObject.m_baseInnerObjectPolymorphic = derivedInnerObject;
  2763. // Create DataPatch using ObjectFieldConverterV1
  2764. DataPatch testPatch;
  2765. const OriginalObjectField initialObjectV1;
  2766. testPatch.Create(&initialObjectV1, &testObject, {}, {}, m_serializeContext.get());
  2767. // Now unreflect the ObjectFieldConverterV1 and reflect ObjectFieldConverterReplaceMemberDataSerializerV2
  2768. {
  2769. m_serializeContext->EnableRemoveReflection();
  2770. OriginalObjectField::Reflect(m_serializeContext.get());
  2771. m_serializeContext->DisableRemoveReflection();
  2772. PatchedObjectField::Reflect(m_serializeContext.get());
  2773. }
  2774. const PatchedObjectField initialObjectV3;
  2775. PatchedObjectField *patchedObjectV3 = testPatch.Apply(&initialObjectV3, m_serializeContext.get());
  2776. ASSERT_NE(nullptr, patchedObjectV3);
  2777. ASSERT_EQ(1, patchedObjectV3->m_rootStringList.size());
  2778. EXPECT_EQ("Test2", patchedObjectV3->m_rootStringList.front());
  2779. // Clean up original ObjectFieldConverterV1 object
  2780. delete testObject.m_baseInnerObjectPolymorphic;
  2781. // Clean up patched ObjectFieldConverterV1 object
  2782. delete patchedObjectV3->m_baseInnerObjectPolymorphic;
  2783. delete patchedObjectV3;
  2784. m_serializeContext->EnableRemoveReflection();
  2785. ObjectWithNumericFieldV1::Reflect(m_serializeContext.get());
  2786. InnerObjectFieldConverterV1::Reflect(m_serializeContext.get());
  2787. InnerObjectFieldConverterDerivedV1::Reflect(m_serializeContext.get());
  2788. PatchedObjectField::Reflect(m_serializeContext.get());
  2789. m_serializeContext->DisableRemoveReflection();
  2790. }
  2791. }
  2792. struct ObjectWithAnyAndBool
  2793. {
  2794. AZ_CLASS_ALLOCATOR(ObjectWithAnyAndBool, SystemAllocator);
  2795. AZ_TYPE_INFO(ObjectWithAnyAndBool, "{266FD5C6-39AE-482F-99B7-DA2A1AFE1EA9}");
  2796. static void Reflect(AZ::ReflectContext* reflectContext)
  2797. {
  2798. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  2799. {
  2800. serializeContext->Class<ObjectWithAnyAndBool>()
  2801. ->Version(1)
  2802. ->Field("AnyValue", &ObjectWithAnyAndBool::m_any)
  2803. ->Field("BoolValue", &ObjectWithAnyAndBool::m_bool);
  2804. }
  2805. }
  2806. AZStd::any m_any;
  2807. bool m_bool;
  2808. };
  2809. TEST_F(PatchingTest, DataPatchingObjectWithAnyPreservesAnyData)
  2810. {
  2811. ObjectWithAnyAndBool::Reflect(m_serializeContext.get());
  2812. ObjectWithAnyAndBool firstObject;
  2813. firstObject.m_any = AZStd::make_any<bool>(false);
  2814. firstObject.m_bool = false;
  2815. ObjectWithAnyAndBool secondObject;
  2816. secondObject.m_any = AZStd::make_any<bool>(false);
  2817. secondObject.m_bool = true;
  2818. DataPatch testPatch;
  2819. testPatch.Create(&firstObject, &secondObject, {}, {}, m_serializeContext.get());
  2820. ObjectWithAnyAndBool* finalObject = testPatch.Apply(&firstObject, m_serializeContext.get());
  2821. ASSERT_FALSE(finalObject->m_any.empty());
  2822. delete finalObject;
  2823. m_serializeContext->EnableRemoveReflection();
  2824. ObjectWithAnyAndBool::Reflect(m_serializeContext.get());
  2825. m_serializeContext->DisableRemoveReflection();
  2826. }
  2827. }
  2828. }