@@ -21,13 +21,24 @@ namespace Apache.Ignite.Core.Tests.Binary
2121 using System . Linq ;
2222 using Apache . Ignite . Core . Binary ;
2323 using Apache . Ignite . Core . Cache . Configuration ;
24+ using Apache . Ignite . Core . Impl . Binary ;
2425 using NUnit . Framework ;
2526
2627 /// <summary>
2728 /// DateTime binary serialization tests.
2829 /// </summary>
2930 public class BinaryDateTimeTest
3031 {
32+ /** */
33+ internal const String FromErrMsg = "FromJavaTicks Error!" ;
34+
35+ /** */
36+ internal const String ToErrMsg = "ToJavaTicks Error!" ;
37+
38+ /** */
39+ private const String NotUtcDate =
40+ "DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms." ;
41+
3142 /// <summary>
3243 /// Sets up the test fixture.
3344 /// </summary>
@@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp()
8091 . Select ( x => x . Serializer )
8192 . OfType < BinaryReflectiveSerializer > ( )
8293 . Single ( ) ;
83-
94+
8495 Assert . IsTrue ( ser . ForceTimestamp ) ;
8596
8697 AssertTimestampField < DateTimeObj2 > ( ( o , d ) => o . Value = d , o => o . Value , "Value" ) ;
@@ -110,6 +121,152 @@ public void TestClassAttributes()
110121 AssertTimestampField < DateTimeClassAttribute2 > ( ( o , d ) => o . Value = d , o => o . Value , "Value" ) ;
111122 }
112123
124+ /// <summary>
125+ /// Tests custom timestamp converter that modifies the values by adding one year on write and read.
126+ /// This test verifies that actual converted values are used by Ignite.
127+ /// </summary>
128+ [ Test ]
129+ public void TestAddYearTimestampConverter ( )
130+ {
131+ var cfg = new IgniteConfiguration ( TestUtils . GetTestConfiguration ( ) )
132+ {
133+ AutoGenerateIgniteInstanceName = true ,
134+ BinaryConfiguration = new BinaryConfiguration
135+ {
136+ ForceTimestamp = true ,
137+ TimestampConverter = new AddYearTimestampConverter ( )
138+ }
139+ } ;
140+
141+ var ignite = Ignition . Start ( cfg ) ;
142+
143+ var dt = DateTime . UtcNow ;
144+ var expected = dt . AddYears ( 2 ) ;
145+
146+ // Key & value.
147+ var cache = ignite . GetOrCreateCache < DateTime , DateTime > ( TestUtils . TestName ) ;
148+ cache [ dt ] = dt ;
149+
150+ var resEntry = cache . Single ( ) ;
151+
152+ Assert . AreEqual ( expected , resEntry . Key ) ;
153+ Assert . AreEqual ( expected , resEntry . Value ) ;
154+
155+ // Key & value array.
156+
157+ // Object field.
158+ var cache2 = ignite . GetOrCreateCache < DateTimePropertyAttribute , DateTimePropertyAttribute > (
159+ TestUtils . TestName ) ;
160+
161+ cache2 . RemoveAll ( ) ;
162+
163+ var obj = new DateTimePropertyAttribute { Value = dt } ;
164+ cache2 [ obj ] = obj ;
165+
166+ var resEntry2 = cache2 . Single ( ) ;
167+ Assert . AreEqual ( expected , resEntry2 . Key . Value ) ;
168+ Assert . AreEqual ( expected , resEntry2 . Value . Value ) ;
169+
170+ // Object array field.
171+ var cache3 = ignite . GetOrCreateCache < DateTimeArr , DateTimeArr > ( TestUtils . TestName ) ;
172+ cache3 . RemoveAll ( ) ;
173+
174+ var obj2 = new DateTimeArr { Value = new [ ] { dt } } ;
175+ cache3 [ obj2 ] = obj2 ;
176+ cache3 [ obj2 ] = obj2 ;
177+
178+ var resEntry3 = cache3 . Single ( ) ;
179+ Assert . AreEqual ( expected , resEntry3 . Key . Value . Single ( ) ) ;
180+ Assert . AreEqual ( expected , resEntry3 . Value . Value . Single ( ) ) ;
181+ }
182+
183+ /// <summary>
184+ /// Tests custom timestamp converter.
185+ /// </summary>
186+ [ Test ]
187+ public void TestCustomTimestampConverter ( )
188+ {
189+ var cfg = new IgniteConfiguration ( TestUtils . GetTestConfiguration ( name : "ignite-1" ) )
190+ {
191+ BinaryConfiguration = new BinaryConfiguration
192+ {
193+ ForceTimestamp = true ,
194+ TimestampConverter = new TimestampConverter ( )
195+ }
196+ } ;
197+
198+ var ignite = Ignition . Start ( cfg ) ;
199+ var binary = ignite . GetBinary ( ) ;
200+
201+ // Check config.
202+ Assert . NotNull ( ignite . GetConfiguration ( ) . BinaryConfiguration . TimestampConverter ) ;
203+
204+ AssertTimestampField < DateTimeObj2 > ( ( o , d ) => o . Value = d , o => o . Value , "Value" , ignite ) ;
205+
206+ var dt1 = new DateTime ( 1997 , 8 , 29 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
207+
208+ var ex = Assert . Throws < BinaryObjectException > ( ( ) => binary . ToBinary < DateTime > ( dt1 ) ) ;
209+ Assert . AreEqual ( ToErrMsg , ex . Message ) ;
210+
211+ ex = Assert . Throws < BinaryObjectException > ( ( ) => binary . ToBinary < DateTime ? [ ] > ( new DateTime ? [ ] { dt1 } ) ) ;
212+ Assert . AreEqual ( ToErrMsg , ex . Message ) ;
213+
214+ var dt2 = new DateTime ( 1997 , 8 , 4 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
215+
216+ ex = Assert . Throws < BinaryObjectException > ( ( ) => binary . ToBinary < DateTime > ( dt2 ) ) ;
217+ Assert . AreEqual ( FromErrMsg , ex . Message ) ;
218+
219+ ex = Assert . Throws < BinaryObjectException > ( ( ) => binary . ToBinary < DateTime ? [ ] > ( new DateTime ? [ ] { dt2 } ) ) ;
220+ Assert . AreEqual ( FromErrMsg , ex . Message ) ;
221+
222+ var datesCache = ignite . CreateCache < DateTime , DateTime > ( "dates" ) ;
223+
224+ var check = new Action < DateTime , DateTime , String > ( ( date1 , date2 , errMsg ) =>
225+ {
226+ ex = Assert . Throws < BinaryObjectException > ( ( ) => datesCache . Put ( date1 , date2 ) , "Timestamp fields should throw an error on non-UTC values" ) ;
227+
228+ Assert . AreEqual ( errMsg , ex . Message ) ;
229+ } ) ;
230+
231+ check . Invoke ( DateTime . Now , DateTime . UtcNow , NotUtcDate ) ;
232+ check . Invoke ( DateTime . UtcNow , DateTime . Now , NotUtcDate ) ;
233+ check . Invoke ( dt1 , DateTime . UtcNow , ToErrMsg ) ;
234+ check . Invoke ( DateTime . UtcNow , dt1 , ToErrMsg ) ;
235+
236+ var now = DateTime . UtcNow ;
237+
238+ datesCache . Put ( now , dt2 ) ;
239+ ex = Assert . Throws < BinaryObjectException > ( ( ) => datesCache . Get ( now ) , "Timestamp fields should throw an error on non-UTC values" ) ;
240+ Assert . AreEqual ( FromErrMsg , ex . Message ) ;
241+
242+ datesCache . Put ( now , now ) ;
243+ Assert . AreEqual ( now , datesCache . Get ( now ) ) ;
244+
245+ var datesObjCache = ignite . CreateCache < DateTimeObj , DateTimeObj > ( "datesObj" ) ;
246+
247+ check = ( date1 , date2 , errMsg ) =>
248+ {
249+ ex = Assert . Throws < BinaryObjectException > ( ( ) => datesObjCache . Put ( new DateTimeObj { Value = date1 } , new DateTimeObj { Value = date2 } ) ,
250+ "Timestamp fields should throw an error on non-UTC values" ) ;
251+
252+ Assert . AreEqual ( errMsg , ex . Message ) ;
253+ } ;
254+
255+ check . Invoke ( DateTime . Now , DateTime . UtcNow , NotUtcDate ) ;
256+ check . Invoke ( DateTime . UtcNow , DateTime . Now , NotUtcDate ) ;
257+ check . Invoke ( dt1 , DateTime . UtcNow , ToErrMsg ) ;
258+ check . Invoke ( DateTime . UtcNow , dt1 , ToErrMsg ) ;
259+
260+ var nowObj = new DateTimeObj { Value = now } ;
261+
262+ datesObjCache . Put ( nowObj , new DateTimeObj { Value = dt2 } ) ;
263+ ex = Assert . Throws < BinaryObjectException > ( ( ) => datesObjCache . Get ( nowObj ) , "Timestamp fields should throw an error on non-UTC values" ) ;
264+ Assert . AreEqual ( FromErrMsg , ex . Message ) ;
265+
266+ datesObjCache . Put ( nowObj , nowObj ) ;
267+ Assert . AreEqual ( nowObj . Value , datesObjCache . Get ( nowObj ) . Value ) ;
268+ }
269+
113270 /// <summary>
114271 /// Asserts that specified field is serialized as DateTime object.
115272 /// </summary>
@@ -136,10 +293,10 @@ private static void AssertDateTimeField<T>(Action<T, DateTime> setValue,
136293 /// Asserts that specified field is serialized as Timestamp.
137294 /// </summary>
138295 private static void AssertTimestampField < T > ( Action < T , DateTime > setValue ,
139- Func < T , DateTime > getValue , string fieldName ) where T : new ( )
296+ Func < T , DateTime > getValue , string fieldName , IIgnite ignite = null ) where T : new ( )
140297 {
141298 // Non-UTC DateTime throws.
142- var binary = Ignition . GetIgnite ( ) . GetBinary ( ) ;
299+ var binary = ignite != null ? ignite . GetBinary ( ) : Ignition . GetIgnite ( ) . GetBinary ( ) ;
143300
144301 var obj = new T ( ) ;
145302
@@ -148,8 +305,7 @@ private static void AssertTimestampField<T>(Action<T, DateTime> setValue,
148305 var ex = Assert . Throws < BinaryObjectException > ( ( ) => binary . ToBinary < IBinaryObject > ( obj ) ,
149306 "Timestamp fields should throw an error on non-UTC values" ) ;
150307
151- Assert . AreEqual ( "DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms." ,
152- ex . Message ) ;
308+ Assert . AreEqual ( NotUtcDate , ex . Message ) ;
153309
154310 // UTC DateTime works.
155311 setValue ( obj , DateTime . UtcNow ) ;
@@ -171,6 +327,11 @@ private class DateTimeObj2
171327 public DateTime Value { get ; set ; }
172328 }
173329
330+ private class DateTimeArr
331+ {
332+ public DateTime [ ] Value { get ; set ; }
333+ }
334+
174335 private class DateTimePropertyAttribute
175336 {
176337 [ Timestamp ]
@@ -200,5 +361,47 @@ private class DateTimeClassAttribute2
200361 {
201362 public DateTime Value ;
202363 }
364+
365+ private class TimestampConverter : ITimestampConverter
366+ {
367+ /** <inheritdoc /> */
368+ public void ToJavaTicks ( DateTime date , out long high , out int low )
369+ {
370+ if ( date . Year == 1997 && date . Month == 8 && date . Day == 29 )
371+ throw new BinaryObjectException ( BinaryDateTimeTest . ToErrMsg ) ;
372+
373+ BinaryUtils . ToJavaDate ( date , out high , out low ) ;
374+ }
375+
376+ /** <inheritdoc /> */
377+ public DateTime FromJavaTicks ( long high , int low )
378+ {
379+ var date = new DateTime ( BinaryUtils . JavaDateTicks + high * TimeSpan . TicksPerMillisecond + low / 100 ,
380+ DateTimeKind . Utc ) ;
381+
382+ if ( date . Year == 1997 && date . Month == 8 && date . Day == 4 )
383+ throw new BinaryObjectException ( BinaryDateTimeTest . FromErrMsg ) ;
384+
385+ return date ;
386+ }
387+ }
388+
389+ private class AddYearTimestampConverter : ITimestampConverter
390+ {
391+ /** <inheritdoc /> */
392+ public void ToJavaTicks ( DateTime date , out long high , out int low )
393+ {
394+ BinaryUtils . ToJavaDate ( date . AddYears ( 1 ) , out high , out low ) ;
395+ }
396+
397+ /** <inheritdoc /> */
398+ public DateTime FromJavaTicks ( long high , int low )
399+ {
400+ var date = new DateTime ( BinaryUtils . JavaDateTicks + high * TimeSpan . TicksPerMillisecond + low / 100 ,
401+ DateTimeKind . Utc ) ;
402+
403+ return date . AddYears ( 1 ) ;
404+ }
405+ }
203406 }
204407}
0 commit comments