Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -556,10 +556,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override bool Step(SQLiteStatement stmt) { + stmt.CheckDisposed(); SQLiteErrorCode n; Random rnd = null; uint starttick = (uint)Environment.TickCount; uint timeout = (uint)(stmt._command._commandTimeout * 1000); @@ -603,10 +604,11 @@ } } internal override SQLiteErrorCode Reset(SQLiteStatement stmt) { + stmt.CheckDisposed(); SQLiteErrorCode n; #if !SQLITE_STANDARD n = UnsafeNativeMethods.sqlite3_reset_interop(stmt._sqlite_stmt); #else @@ -645,10 +647,11 @@ return SQLiteBase.GetLastError(_sql, _sql); } internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, out string strRemain) { + if (previous != null) previous.CheckDisposed(); if (!String.IsNullOrEmpty(strSql)) { // // NOTE: SQLite does not support the concept of separate schemas // in one database; therefore, remove the base schema name @@ -885,10 +888,11 @@ handleIntPtr, index, typeof(Byte[]), (value != null) ? ToHexadecimalString(value) : "")); } internal override void Bind_Double(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, double value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { LogBind(handle, index, value); @@ -904,10 +908,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_Int32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, int value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { LogBind(handle, index, value); @@ -917,10 +922,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_UInt32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, uint value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -949,10 +955,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_Int64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, long value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -968,10 +975,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_UInt64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, ulong value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -987,10 +995,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1011,10 +1020,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1099,10 +1109,11 @@ } } internal override void Bind_Blob(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, byte[] blobData) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1114,10 +1125,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override void Bind_Null(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1129,10 +1141,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override int Bind_ParamCount(SQLiteStatement stmt, SQLiteConnectionFlags flags) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; int value = UnsafeNativeMethods.sqlite3_bind_parameter_count(handle); if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1147,10 +1160,11 @@ return value; } internal override string Bind_ParamName(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; string name; #if !SQLITE_STANDARD int len; @@ -1172,10 +1186,11 @@ return name; } internal override int Bind_ParamIndex(SQLiteStatement stmt, SQLiteConnectionFlags flags, string paramName) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; int index = UnsafeNativeMethods.sqlite3_bind_parameter_index(handle, ToUTF8(paramName)); if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -1190,15 +1205,17 @@ return index; } internal override int ColumnCount(SQLiteStatement stmt) { + stmt.CheckDisposed(); return UnsafeNativeMethods.sqlite3_column_count(stmt._sqlite_stmt); } internal override string ColumnName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_name_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF8ToString(UnsafeNativeMethods.sqlite3_column_name(stmt._sqlite_stmt, index), -1); @@ -1205,15 +1222,17 @@ #endif } internal override TypeAffinity ColumnAffinity(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return UnsafeNativeMethods.sqlite3_column_type(stmt._sqlite_stmt, index); } internal override string ColumnType(SQLiteStatement stmt, int index, out TypeAffinity nAffinity) { + stmt.CheckDisposed(); int len; #if !SQLITE_STANDARD IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype_interop(stmt._sqlite_stmt, index, out len); #else len = -1; @@ -1246,10 +1265,11 @@ } } internal override int ColumnIndex(SQLiteStatement stmt, string columnName) { + stmt.CheckDisposed(); int x = ColumnCount(stmt); for (int n = 0; n < x; n++) { if (String.Compare(columnName, ColumnName(stmt, n), StringComparison.OrdinalIgnoreCase) == 0) @@ -1258,10 +1278,11 @@ return -1; } internal override string ColumnOriginalName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name(stmt._sqlite_stmt, index), -1); @@ -1268,10 +1289,11 @@ #endif } internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name(stmt._sqlite_stmt, index), -1); @@ -1278,10 +1300,11 @@ #endif } internal override string ColumnTableName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name(stmt._sqlite_stmt, index), -1); @@ -1317,10 +1340,11 @@ autoIncrement = (nautoInc == 1); } internal override double GetDouble(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); double value; #if !PLATFORM_COMPACTFRAMEWORK value = UnsafeNativeMethods.sqlite3_column_double(stmt._sqlite_stmt, index); #elif !SQLITE_STANDARD UnsafeNativeMethods.sqlite3_column_double_interop(stmt._sqlite_stmt, index, out value); @@ -1330,40 +1354,47 @@ return value; } internal override sbyte GetSByte(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((sbyte)(GetInt32(stmt, index) & byte.MaxValue)); } internal override byte GetByte(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((byte)(GetInt32(stmt, index) & byte.MaxValue)); } internal override short GetInt16(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((short)(GetInt32(stmt, index) & ushort.MaxValue)); } internal override ushort GetUInt16(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((ushort)(GetInt32(stmt, index) & ushort.MaxValue)); } internal override int GetInt32(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return UnsafeNativeMethods.sqlite3_column_int(stmt._sqlite_stmt, index); } internal override uint GetUInt32(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((uint)GetInt32(stmt, index)); } internal override long GetInt64(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); long value; #if !PLATFORM_COMPACTFRAMEWORK value = UnsafeNativeMethods.sqlite3_column_int64(stmt._sqlite_stmt, index); #elif !SQLITE_STANDARD UnsafeNativeMethods.sqlite3_column_int64_interop(stmt._sqlite_stmt, index, out value); @@ -1373,15 +1404,17 @@ return value; } internal override ulong GetUInt64(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return unchecked((ulong)GetInt64(stmt, index)); } internal override string GetText(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), @@ -1389,10 +1422,11 @@ #endif } internal override DateTime GetDateTime(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); if (_datetimeFormat == SQLiteDateFormats.Ticks) return ToDateTime(GetInt64(stmt, index), _datetimeKind); else if (_datetimeFormat == SQLiteDateFormats.JulianDay) return ToDateTime(GetDouble(stmt, index), _datetimeKind); else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch) @@ -1407,10 +1441,11 @@ #endif } internal override long GetBytes(SQLiteStatement stmt, int index, int nDataOffset, byte[] bDest, int nStart, int nLength) { + stmt.CheckDisposed(); int nlen = UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index); // If no destination buffer, return the size needed. if (bDest == null) return nlen; @@ -1433,10 +1468,11 @@ return nCopied; } internal override long GetChars(SQLiteStatement stmt, int index, int nDataOffset, char[] bDest, int nStart, int nLength) { + stmt.CheckDisposed(); int nlen; int nCopied = nLength; string str = GetText(stmt, index); nlen = str.Length; @@ -1453,10 +1489,11 @@ return nCopied; } internal override bool IsNull(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); return (ColumnAffinity(stmt, index) == TypeAffinity.Null); } internal override int AggregateCount(IntPtr context) { @@ -2343,10 +2380,11 @@ /// The column index to retrieve /// The type of data contained in the column. If Uninitialized, this function will retrieve the datatype information. /// Returns the data in the column internal override object GetValue(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, SQLiteType typ) { + stmt.CheckDisposed(); if (IsNull(stmt, index)) return DBNull.Value; TypeAffinity aff = typ.Affinity; Type t = null; if (typ.Type != DbType.Object) @@ -2392,19 +2430,21 @@ } } internal override int GetCursorForTable(SQLiteStatement stmt, int db, int rootPage) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD return UnsafeNativeMethods.sqlite3_table_cursor_interop(stmt._sqlite_stmt, db, rootPage); #else return -1; #endif } internal override long GetRowIdForCursor(SQLiteStatement stmt, int cursor) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD long rowid; SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_cursor_rowid_interop(stmt._sqlite_stmt, cursor, out rowid); if (rc == SQLiteErrorCode.Ok) return rowid; Index: System.Data.SQLite/SQLite3_UTF16.cs ================================================================== --- System.Data.SQLite/SQLite3_UTF16.cs +++ System.Data.SQLite/SQLite3_UTF16.cs @@ -209,10 +209,11 @@ GC.KeepAlive(_sql); } internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt) { + stmt.CheckDisposed(); switch (_datetimeFormat) { case SQLiteDateFormats.Ticks: case SQLiteDateFormats.JulianDay: case SQLiteDateFormats.UnixEpoch: @@ -238,10 +239,11 @@ } } internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value) { + stmt.CheckDisposed(); SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { @@ -253,10 +255,11 @@ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); } internal override DateTime GetDateTime(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); if (_datetimeFormat == SQLiteDateFormats.Ticks) return ToDateTime(GetInt64(stmt, index), _datetimeKind); else if (_datetimeFormat == SQLiteDateFormats.JulianDay) return ToDateTime(GetDouble(stmt, index), _datetimeKind); else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch) @@ -265,10 +268,11 @@ return ToDateTime(GetText(stmt, index)); } internal override string ColumnName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index), -1); @@ -275,10 +279,11 @@ #endif } internal override string GetText(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index), @@ -286,10 +291,11 @@ #endif } internal override string ColumnOriginalName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1); @@ -296,10 +302,11 @@ #endif } internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1); @@ -306,10 +313,11 @@ #endif } internal override string ColumnTableName(SQLiteStatement stmt, int index) { + stmt.CheckDisposed(); #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1); Index: System.Data.SQLite/SQLiteConvert.cs ================================================================== --- System.Data.SQLite/SQLiteConvert.cs +++ System.Data.SQLite/SQLiteConvert.cs @@ -785,10 +785,11 @@ /// The statement to retrieve information for /// The column to retrieve type information on /// The SQLiteType to receive the affinity for the given column internal static void ColumnToType(SQLiteStatement stmt, int i, SQLiteType typ) { + stmt.CheckDisposed(); typ.Type = TypeNameToDbType(stmt._sql.ColumnType(stmt, i, out typ.Affinity)); } /// /// Converts a SQLiteType to a .NET Type object Index: System.Data.SQLite/SQLiteKeyReader.cs ================================================================== --- System.Data.SQLite/SQLiteKeyReader.cs +++ System.Data.SQLite/SQLiteKeyReader.cs @@ -148,11 +148,12 @@ /// /// /// /// internal SQLiteKeyReader(SQLiteConnection cnn, SQLiteDataReader reader, SQLiteStatement stmt) - { + { + stmt.CheckDisposed(); Dictionary catalogs = new Dictionary(); Dictionary> tables = new Dictionary>(); List list; List keys = new List(); Index: System.Data.SQLite/SQLiteParameterCollection.cs ================================================================== --- System.Data.SQLite/SQLiteParameterCollection.cs +++ System.Data.SQLite/SQLiteParameterCollection.cs @@ -410,11 +410,12 @@ /// This function attempts to map all parameters in the collection to all statements in a Command. /// Since named parameters may span multiple statements, this function makes sure all statements are bound /// to the same named parameter. Unnamed parameters are bound in sequence. /// internal void MapParameters(SQLiteStatement activeStatement) - { + { + if (activeStatement != null) activeStatement.CheckDisposed(); if (_unboundFlag == false || _parameterList.Count == 0 || _command._statementList == null) return; int nUnnamed = 0; string s; int n; Index: System.Data.SQLite/SQLiteStatement.cs ================================================================== --- System.Data.SQLite/SQLiteStatement.cs +++ System.Data.SQLite/SQLiteStatement.cs @@ -58,11 +58,13 @@ /// The flags associated with the parent connection object /// The statement /// The command text for this statement /// The previous command in a multi-statement command internal SQLiteStatement(SQLiteBase sqlbase, SQLiteConnectionFlags flags, SQLiteStatementHandle stmt, string strCommand, SQLiteStatement previous) - { + { + if (previous != null) previous.CheckDisposed(); + _sql = sqlbase; _sqlite_stmt = stmt; _sqlStatement = strCommand; _flags = flags; @@ -110,11 +112,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; - private void CheckDisposed() /* throw */ + internal void CheckDisposed() /* throw */ { #if THROW_ON_DISPOSED if (disposed) throw new ObjectDisposedException(typeof(SQLiteStatement).Name); #endif ADDED Tests/tkt-5363fd4af6.eagle Index: Tests/tkt-5363fd4af6.eagle ================================================================== --- /dev/null +++ Tests/tkt-5363fd4af6.eagle @@ -0,0 +1,99 @@ +############################################################################### +# +# tkt-5363fd4af6.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-5363fd4af6-1.1 {data reader access violation} -setup { + set fileName tkt-5363fd4af6-1.1.db +} -body { + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + + set sql { \ + CREATE TABLE t1(x); \ + INSERT INTO t1 (x) VALUES(1); \ + INSERT INTO t1 (x) VALUES(2); \ + INSERT INTO t1 (x) VALUES(3); \ + SELECT x FROM t1 ORDER BY x; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data.SQLite; + using Eagle._Containers.Public; + + namespace _Dynamic${id} + { + public static class Test${id} + { + public static StringList GetList() + { + StringList result = new StringList(); + + using (SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};")) + { + connection.Open(); + + SQLiteCommand command = new SQLiteCommand("${sql}", connection); + SQLiteDataReader dataReader = command.ExecuteReader(); + + command.Dispose(); + GC.GetTotalMemory(true); + + while (dataReader.Read()) + result.Add(dataReader\["x"\].ToString()); + + dataReader.Dispose(); + } + + return result; + } + + /////////////////////////////////////////////////////////////////////// + + public static void Main() + { + // do nothing. + } + } + } + }] true true true results errors [list System.Data.SQLite.dll Eagle.dll]] + + list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} GetList + } result] : [set result ""]}] $result +} -cleanup { + cleanupDb $fileName + + unset -nocomplain result results errors code sql dataSource id fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \ +-match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0\ +\{1 2 3\}$}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue