2020
2121import static org .mockito .Mockito .when ;
2222
23+ import java .lang .reflect .Field ;
2324import java .net .URI ;
2425import java .net .URISyntaxException ;
26+ import java .text .SimpleDateFormat ;
2527import java .util .ArrayList ;
2628import java .util .Arrays ;
2729import java .util .Date ;
3335import java .util .concurrent .ConcurrentHashMap ;
3436import java .util .concurrent .TimeUnit ;
3537
38+ import com .cloud .utils .DateUtil ;
39+ import com .google .gson .JsonSyntaxException ;
3640import org .apache .cloudstack .framework .config .ConfigKey ;
3741import org .apache .cloudstack .storage .datastore .db .StoragePoolVO ;
3842import org .apache .commons .collections .CollectionUtils ;
@@ -116,6 +120,8 @@ public class StatsCollectorTest {
116120
117121 private static Gson gson = new Gson ();
118122
123+ private Gson msStatsGson ;
124+
119125 private MockedStatic <InfluxDBFactory > influxDBFactoryMocked ;
120126
121127 private AutoCloseable closeable ;
@@ -125,6 +131,9 @@ public void setUp() throws Exception {
125131 closeable = MockitoAnnotations .openMocks (this );
126132 statsCollector .vmStatsDao = vmStatsDaoMock ;
127133 statsCollector .volumeStatsDao = volumeStatsDao ;
134+ Field msStatsGsonField = StatsCollector .class .getDeclaredField ("msStatsGson" );
135+ msStatsGsonField .setAccessible (true );
136+ msStatsGson = (Gson ) msStatsGsonField .get (null );
128137 }
129138
130139 @ After
@@ -612,4 +621,107 @@ public void testPoolNeedsIopsStatsUpdating_NullIops() {
612621 Mockito .verify (mockPool , Mockito .never ()).setCapacityIops (Mockito .anyLong ());
613622 Mockito .verify (mockPool , Mockito .never ()).setUsedIops (Mockito .anyLong ());
614623 }
624+
625+ @ Test
626+ public void testGsonDateFormatSerialization () {
627+ Date now = new Date ();
628+ TestClass testObj = new TestClass ("TestString" , 999 , now );
629+ String json = msStatsGson .toJson (testObj );
630+
631+ Assert .assertTrue (json .contains ("TestString" ));
632+ Assert .assertTrue (json .contains ("999" ));
633+ String expectedDate = new SimpleDateFormat (DateUtil .ZONED_DATETIME_FORMAT ).format (now );
634+ Assert .assertTrue (json .contains (expectedDate ));
635+ }
636+
637+ @ Test
638+ public void testGsonDateFormatDeserializationWithSameDateFormat () throws Exception {
639+ String json = "{\" str\" :\" TestString\" ,\" num\" :999,\" date\" :\" 2025-08-22T15:39:43+0000\" }" ;
640+ TestClass testObj = msStatsGson .fromJson (json , TestClass .class );
641+
642+ Assert .assertEquals ("TestString" , testObj .getStr ());
643+ Assert .assertEquals (999 , testObj .getNum ());
644+ Date expectedDate = new SimpleDateFormat (DateUtil .ZONED_DATETIME_FORMAT ).parse ("2025-08-22T15:39:43+0000" );
645+ Assert .assertEquals (expectedDate , testObj .getDate ());
646+ }
647+
648+ @ Test (expected = JsonSyntaxException .class )
649+ public void testGsonDateFormatDeserializationWithDifferentDateFormat () throws Exception {
650+ String json = "{\" str\" :\" TestString\" ,\" num\" :999,\" date\" :\" 22/08/2025T15:39:43+0000\" }" ;
651+ msStatsGson .fromJson (json , TestClass .class );
652+ /* Deserialization throws the below exception:
653+ com.google.gson.JsonSyntaxException: 22/08/2025T15:39:43+0000
654+ at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:376)
655+ at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:351)
656+ at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserialize(DefaultTypeAdapters.java:307)
657+ at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
658+ at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
659+ at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
660+ at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
661+ at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
662+ at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
663+ at com.google.gson.Gson.fromJson(Gson.java:551)
664+ at com.google.gson.Gson.fromJson(Gson.java:498)
665+ at com.google.gson.Gson.fromJson(Gson.java:467)
666+ at com.google.gson.Gson.fromJson(Gson.java:417)
667+ at com.google.gson.Gson.fromJson(Gson.java:389)
668+ at com.cloud.serializer.GsonHelperTest.testGsonDateFormatDeserializationWithDifferentDateFormat(GsonHelperTest.java:113)
669+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
670+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
671+ at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
672+ at java.base/java.lang.reflect.Method.invoke(Method.java:566)
673+ at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
674+ at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
675+ at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
676+ at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
677+ at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
678+ at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
679+ at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
680+ at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
681+ at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
682+ at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
683+ at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
684+ at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
685+ at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
686+ at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
687+ at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
688+ at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
689+ at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
690+ at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
691+ at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
692+ at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
693+ at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
694+ at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
695+ at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)
696+ at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
697+ Caused by: java.text.ParseException: Unparseable date: "22/08/2025T15:39:43+0000"
698+ at java.base/java.text.DateFormat.parse(DateFormat.java:395)
699+ at com.google.gson.DefaultTypeAdapters$DefaultDateTypeAdapter.deserializeToDate(DefaultTypeAdapters.java:374)
700+ ... 42 more
701+ */
702+ }
703+
704+ private static class TestClass {
705+ private String str ;
706+ private int num ;
707+ private Date date ;
708+
709+ public TestClass (String str , int num , Date date ) {
710+ this .str = str ;
711+ this .num = num ;
712+ this .date = date ;
713+ }
714+
715+ public String getStr () {
716+ return str ;
717+ }
718+
719+ public int getNum () {
720+ return num ;
721+ }
722+
723+ public Date getDate () {
724+ return date ;
725+ }
726+ }
615727}
0 commit comments