System.Data.SQLite
Check-in [ca9127aa51]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add and revise tests for the session extension support classes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: ca9127aa513e34d70c7f6595b5b6af565fb17477
User & Date: mistachkin 2017-10-10 20:08:56
Original Comment: Add and revise tests for the session extension.
Context
2017-10-10
21:38
Heavily refactor how native delegates and exception handling are performed. check-in: 3b281ced11 user: mistachkin tags: sessions
20:08
Add and revise tests for the session extension support classes. check-in: ca9127aa51 user: mistachkin tags: sessions
05:22
Add initial tests for the session extension support classes. check-in: 46f407ce75 user: mistachkin tags: sessions
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to System.Data.SQLite/UnsafeNativeMethods.cs.

226
227
228
229
230
231
232

























233
234
235
236
237
238
239
...
333
334
335
336
337
338
339























































340
341
342
343
344
345
346
  #region Helper Methods Static Class
  /// <summary>
  /// This static class provides some methods that are shared between the
  /// native library pre-loader and other classes.
  /// </summary>
  internal static class HelperMethods
  {

























      #region Private Data
      /// <summary>
      /// This lock is used to protect the static <see cref="isMono" /> field.
      /// </summary>
      private static readonly object staticSyncRoot = new object();

      /////////////////////////////////////////////////////////////////////////
................................................................................
      {
          if (IsMono())
              return String.Format(format, args);
          else
              return String.Format(provider, format, args);
      }
      #endregion























































  }
  #endregion

  /////////////////////////////////////////////////////////////////////////////

  #region Native Library Helper Class
  /// <summary>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  #region Helper Methods Static Class
  /// <summary>
  /// This static class provides some methods that are shared between the
  /// native library pre-loader and other classes.
  /// </summary>
  internal static class HelperMethods
  {
      #region Private Constants
      private const string DisplayNullObject = "<nullObject>";
      private const string DisplayEmptyString = "<emptyString>";
      private const string DisplayStringFormat = "\"{0}\"";

      /////////////////////////////////////////////////////////////////////////

      private const string DisplayNullArray = "<nullArray>";
      private const string DisplayEmptyArray = "<emptyArray>";

      /////////////////////////////////////////////////////////////////////////

      private const char ArrayOpen = '[';
      private const string ElementSeparator = ", ";
      private const char ArrayClose = ']';

      /////////////////////////////////////////////////////////////////////////

      private static readonly char[] SpaceChars = {
          '\t', '\n', '\r', '\v', '\f', ' '
      };
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region Private Data
      /// <summary>
      /// This lock is used to protect the static <see cref="isMono" /> field.
      /// </summary>
      private static readonly object staticSyncRoot = new object();

      /////////////////////////////////////////////////////////////////////////
................................................................................
      {
          if (IsMono())
              return String.Format(format, args);
          else
              return String.Format(provider, format, args);
      }
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region Public Methods
      public static string ToDisplayString(
          object value
          )
      {
          if (value == null)
              return DisplayNullObject;

          string stringValue = value.ToString();

          if (stringValue.Length == 0)
              return DisplayEmptyString;

          if (stringValue.IndexOfAny(SpaceChars) < 0)
              return stringValue;

          return HelperMethods.StringFormat(
              CultureInfo.InvariantCulture, DisplayStringFormat,
              stringValue);
      }

      /////////////////////////////////////////////////////////////////////////

      public static string ToDisplayString(
          Array array
          )
      {
          if (array == null)
              return DisplayNullArray;

          if (array.Length == 0)
              return DisplayEmptyArray;

          StringBuilder result = new StringBuilder();

          foreach (object value in array)
          {
              if (result.Length > 0)
                  result.Append(ElementSeparator);

              result.Append(ToDisplayString(value));
          }

          if (result.Length > 0)
          {
              result.Insert(0, ArrayOpen);
              result.Append(ArrayClose);
          }

          return result.ToString();
      }
      #endregion
  }
  #endregion

  /////////////////////////////////////////////////////////////////////////////

  #region Native Library Helper Class
  /// <summary>

Changes to Tests/session.eagle.

16
17
18
19
20
21
22
23
24
25
26
27
28
29









30
31
32











33
34
35
36
37
38
39
40
41







































































42
43
44
45
46
47
48
49
50
51
52
53


54
55
56
57
58


59
60


61
62
63
64
65
66
67
68
69
...
113
114
115
116
117
118
119


120

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
...
143
144
145
146
147
148
149


150
151
152
153
154
155
156
...
162
163
164
165
166
167
168


























































169
170
171
172
173



174
175
176
177
178
179
###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

proc getRandomText { {count 5} } {
  set items [list \
      Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel \
      India Juliet Kilo Lima Mike November Oscar Papa \
      Quebec Romeo Sierra Tango Uniform Victor Whiskey X-ray \
      Yankee Zulu]










  set result [list]

  for {set i 0} {$i < $count} {incr i} {











    lappend result [lindex $items \
        [expr {int(rand() * [llength $items])}]]
  }

  return $result
}

###############################################################################








































































proc createSchema { db } {
  sql execute $db {
    CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
  }
}

###############################################################################

proc makeChanges { db types } {
  foreach type $types {
    switch -nocase -- $type {
      insert {


        sql execute $db {
          INSERT INTO t1(y) VALUES(?);
        } [list param1 String [getRandomText]]
      }
      update {


        sql execute $db {
          UPDATE t1 SET y = ? WHERE x = (SELECT MAX(x) FROM t1);


        } [list param1 String [appendArgs \
            "updated: " [getRandomText]]]
      }
      delete {
        sql execute $db {
          DELETE FROM t1 WHERE x = (SELECT MIN(x) FROM t1);
        }
      }
    }
................................................................................
###############################################################################

runTest {test session-1.1 {basic session extension usage} -setup {
  setupDb [set fileName(0) session-1.1.db]

  set fileName(1) [getChangeSetFileName 1]
  set fileName(2) [getChangeSetFileName 2]


} -body {

  createSchema $db; makeChanges $db [list insert]

  set connection [getDbConnection]

  set session [$connection -alias CreateSession main]
  $session AttachTable null

  makeChanges $db [list insert update delete]

  set byteArray null
  set changeSet(1) [$session -alias CreateChangeSet byteArray]
  set rawData [createByteArray [arrayToList byteArray]]
  object removeref $rawData

  set stream(1) [object create -alias \
................................................................................

  set changeSet(2) [$session -alias CreateChangeSet $stream(2)]
  $stream(2) Flush; $stream(2) Close

  list [expr {[file size $fileName(1)] > 0}] \
      [string equal [readFile $fileName(1)] [readFile $fileName(2)]]
} -cleanup {


  unset -nocomplain changeSet stream rawData byteArray session

  freeDbConnection

  unset -nocomplain connection

  cleanupFile $fileName(2)
................................................................................
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite SQLiteInterop\
defineConstant.System.Data.SQLite.INTEROP_SESSION_EXTENSION} -result \
{True True}}

###############################################################################



























































rename createByteArray ""
rename arrayToList ""
rename getChangeSetFileName ""
rename makeChanges ""
rename createSchema ""



rename getRandomText ""

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue







|






>
>
>
>
>
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
|
<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







|



>
>


|


>
>

|
>
>
|
<







 







>
>

>
|






|







 







>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|
|
>
>
>
|





16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

proc getSomeText { random count } {
  set items [list \
      Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel \
      India Juliet Kilo Lima Mike November Oscar Papa \
      Quebec Romeo Sierra Tango Uniform Victor Whiskey X-ray \
      Yankee Zulu]

  if {!$random} then {
    variable some_text_index

    if {![info exists some_text_index]} then {
      set some_text_index 0
    }
  }

  set length [llength $items]
  set result [list]

  for {set i 0} {$i < $count} {incr i} {
    if {$random} then {
      set item [lindex $items [expr {int(rand() * $length)}]]
    } else {
      set item [lindex $items $some_text_index]
      incr some_text_index

      if {$some_text_index >= $length} then {
        set some_text_index 0
      }
    }

    lappend result $item

  }

  return $result
}

###############################################################################

proc cleanupSomeText {} {
  variable some_text_index
  unset -nocomplain some_text_index
}

###############################################################################

proc forDisplay { object member } {
  set isArray false

  if {[isObjectHandle $object] && $object ne "null"} then {
    set value [object invoke -create -- $object $member]

    if {[isObjectHandle $value] && $value ne "null"} then {
      set isArray [object invoke -- $value GetType.IsArray]
    }
  } else {
    set value null
  }

  if {$isArray} then {
    return [object invoke -flags +NonPublic \
        -parametertypes Array System.Data.SQLite.HelperMethods \
        ToDisplayString $value]
  } else {
    return [object invoke -flags +NonPublic \
        System.Data.SQLite.HelperMethods ToDisplayString $value]
  }
}

###############################################################################

proc changeSetToString { changeSet includeValues } {
  set result [list]

  object foreach -alias item $changeSet {
    lappend result TableName [$item TableName]
    lappend result NumberOfColumns [$item NumberOfColumns]
    lappend result OperationCode [$item OperationCode]
    lappend result Indirect [$item Indirect]

    lappend result PrimaryKeyColumns \
        [forDisplay $item PrimaryKeyColumns]

    if {$includeValues} then {
      set numberOfColumns [$item NumberOfColumns]

      for {set index 0} {$index < $numberOfColumns} {incr index} {
        set oldValue [$item GetOldValue $index]

        lappend result OldValue $index \
            [forDisplay $oldValue GetObject]

        set newValue [$item GetNewValue $index]

        lappend result NewValue $index \
            [forDisplay $newValue GetObject]

        set conflictValue [$item GetConflictValue $index]

        lappend result ConflictValue $index \
            [forDisplay $conflictValue GetObject]
      }
    }
  }

  return $result
}

###############################################################################

proc createTheSchema { db } {
  sql execute $db {
    CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
  }
}

###############################################################################

proc makeSomeChanges { db types random {count 5} } {
  foreach type $types {
    switch -nocase -- $type {
      insert {
        set text [appendArgs "inserted: " [getSomeText $random $count]]

        sql execute $db {
          INSERT INTO t1(y) VALUES(?);
        } [list param1 String $text]
      }
      update {
        set text [appendArgs "updated: " [getSomeText $random $count]]

        sql execute $db {
          UPDATE t1 SET y = ? WHERE x NOT IN (
            (SELECT MIN(x) FROM t1), (SELECT MAX(x) FROM t1)
          );
        } [list param1 String $text]

      }
      delete {
        sql execute $db {
          DELETE FROM t1 WHERE x = (SELECT MIN(x) FROM t1);
        }
      }
    }
................................................................................
###############################################################################

runTest {test session-1.1 {basic session extension usage} -setup {
  setupDb [set fileName(0) session-1.1.db]

  set fileName(1) [getChangeSetFileName 1]
  set fileName(2) [getChangeSetFileName 2]

  cleanupSomeText
} -body {
  createTheSchema $db
  makeSomeChanges $db [list insert] true

  set connection [getDbConnection]

  set session [$connection -alias CreateSession main]
  $session AttachTable null

  makeSomeChanges $db [list insert update delete] true

  set byteArray null
  set changeSet(1) [$session -alias CreateChangeSet byteArray]
  set rawData [createByteArray [arrayToList byteArray]]
  object removeref $rawData

  set stream(1) [object create -alias \
................................................................................

  set changeSet(2) [$session -alias CreateChangeSet $stream(2)]
  $stream(2) Flush; $stream(2) Close

  list [expr {[file size $fileName(1)] > 0}] \
      [string equal [readFile $fileName(1)] [readFile $fileName(2)]]
} -cleanup {
  cleanupSomeText

  unset -nocomplain changeSet stream rawData byteArray session

  freeDbConnection

  unset -nocomplain connection

  cleanupFile $fileName(2)
................................................................................
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite SQLiteInterop\
defineConstant.System.Data.SQLite.INTEROP_SESSION_EXTENSION} -result \
{True True}}

###############################################################################

runTest {test session-1.2 {session change set enumeration} -setup {
  setupDb [set fileName session-1.2.db]

  cleanupSomeText
} -body {
  createTheSchema $db
  makeSomeChanges $db [list insert insert insert] false

  set connection [getDbConnection]

  set session [$connection -alias CreateSession main]
  $session AttachTable null

  makeSomeChanges $db [list insert update delete] false

  set byteArray null
  set changeSet(1) [$session -alias CreateChangeSet byteArray]
  set rawData [createByteArray [arrayToList byteArray]]
  object removeref $rawData

  set changeSet(2) [$connection -alias CreateChangeSet $rawData]

  changeSetToString $changeSet(2) true
} -cleanup {
  cleanupSomeText

  unset -nocomplain oldValue newValue conflictValue
  unset -nocomplain changeSet rawData byteArray session

  freeDbConnection

  unset -nocomplain connection

  cleanupDb $fileName

  unset -nocomplain db fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite SQLiteInterop\
defineConstant.System.Data.SQLite.INTEROP_SESSION_EXTENSION} -result {TableName\
t1 NumberOfColumns 2 OperationCode Delete Indirect False PrimaryKeyColumns\
{[True, False]} OldValue 0 1 NewValue 0 <nullObject> ConflictValue 0\
<nullObject> OldValue 1 {"inserted: Alpha Bravo Charlie Delta Echo"} NewValue 1\
<nullObject> ConflictValue 1 <nullObject> TableName t1 NumberOfColumns 2\
OperationCode Update Indirect False PrimaryKeyColumns {[True, False]} OldValue\
0 2 NewValue 0 <nullObject> ConflictValue 0 <nullObject> OldValue 1 {"inserted:\
Foxtrot Golf Hotel India Juliet"} NewValue 1 {"updated: Uniform Victor Whiskey\
X-ray Yankee"} ConflictValue 1 <nullObject> TableName t1 NumberOfColumns 2\
OperationCode Update Indirect False PrimaryKeyColumns {[True, False]} OldValue\
0 3 NewValue 0 <nullObject> ConflictValue 0 <nullObject> OldValue 1 {"inserted:\
Kilo Lima Mike November Oscar"} NewValue 1 {"updated: Uniform Victor Whiskey\
X-ray Yankee"} ConflictValue 1 <nullObject> TableName t1 NumberOfColumns 2\
OperationCode Insert Indirect False PrimaryKeyColumns {[True, False]} OldValue\
0 <nullObject> NewValue 0 4 ConflictValue 0 <nullObject> OldValue 1\
<nullObject> NewValue 1 {"inserted: Papa Quebec Romeo Sierra Tango"}\
ConflictValue 1 <nullObject>}}

###############################################################################

rename createByteArray ""
rename arrayToList ""
rename getChangeSetFileName ""
rename makeSomeChanges ""
rename createTheSchema ""
rename changeSetToString ""
rename forDisplay ""
rename cleanupSomeText ""
rename getSomeText ""

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue