Index: Externals/Eagle/lib/Eagle1.0/vendor.eagle ================================================================== --- Externals/Eagle/lib/Eagle1.0/vendor.eagle +++ Externals/Eagle/lib/Eagle1.0/vendor.eagle @@ -31,10 +31,27 @@ # support namespaces ourselves, we do not want to pollute the global # namespace if this script actually ends up being evaluated in Tcl. # namespace eval ::Eagle { if {[isEagle]} then { + proc checkForVendorQuiet { {name ""} } { + if {[info exists ::env(checkForVendorQuiet)]} then { + return true + } + + if {[string length $name] > 0} then { + set envVarName [appendArgs quiet [string toupper \ + [string index $name 0]] [string range $name 1 end]] + + if {[info exists ::env($envVarName)]} then { + return true + } + } + + return false + } + proc checkForTestOverrides { channel varNames quiet } { set result 0 foreach varName $varNames { if {![uplevel 1 [list info exists $varName]]} then { @@ -343,11 +360,11 @@ test_clr_v4 test_configuration test_configurations test_constraints \ test_machine test_net_fx test_net_fx_2005 test_net_fx_2008 \ test_net_fx_2010 test_net_fx_2012 test_net_fx_2013 test_net_fx_2015 \ test_overrides test_platform test_suite test_year test_years \ test_year_clr_v2 test_year_clr_v4 vendor_directory \ - vendor_test_directory]}] false + vendor_test_directory]}] [checkForVendorQuiet checkForTestOverrides] # # NOTE: Set the name of the running test suite, if necessary. # if {![info exists test_suite]} then { @@ -391,11 +408,11 @@ # however, this will not be done if the variable was not created # by us. # addTestSuiteToAutoPath stdout \ [expr {$have_vendor_directory ? "" : "vendor_directory"}] \ - [info exists ::env(quietAddTestSuiteToAutoPath)] + [checkForVendorQuiet addTestSuiteToAutoPath] unset have_vendor_directory # # NOTE: This procedure will attempt to find the vendor-specific testing @@ -403,22 +420,22 @@ # above to point to the directory; however, this will not be done # if the variable was not created by us. # findInterpreterTestPath stdout $vendor_directory \ [expr {$have_vendor_test_directory ? "" : "vendor_test_directory"}] \ - [info exists ::env(quietFindInterpreterTestPath)] + [checkForVendorQuiet findInterpreterTestPath] unset have_vendor_test_directory # # NOTE: If we actually found a vendor-specific testing infrastructure # directory then modify the TestPath property of the current # interpreter to point directly to it. # if {[string length $vendor_test_directory] > 0} then { - setupInterpreterTestPath stdout $vendor_test_directory [info exists \ - ::env(quietSetupInterpreterTestPath)] + setupInterpreterTestPath stdout $vendor_test_directory \ + [checkForVendorQuiet setupInterpreterTestPath] } } # # HACK: Prevent the Eagle core test suite infrastructure from checking Index: Setup/data/verify.lst ================================================================== --- Setup/data/verify.lst +++ Setup/data/verify.lst @@ -832,10 +832,11 @@ Tests/template/empty.eagle Tests/thread.eagle Tests/tkt-00f86f9739.eagle Tests/tkt-0a32885109.eagle Tests/tkt-0d5b1ef362.eagle + Tests/tkt-0e48e80333.eagle Tests/tkt-0ed01c447c.eagle Tests/tkt-17045010df.eagle Tests/tkt-1c456ae75f.eagle Tests/tkt-1f7bfff467.eagle Tests/tkt-201128cc88.eagle Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -65,10 +65,11 @@ /// The opaque pointer returned to us by the sqlite provider /// protected internal SQLiteConnectionHandle _sql; protected string _fileName; protected SQLiteConnectionFlags _flags; + private bool _setLogCallback; protected bool _usePool; protected int _poolVersion; private int _cancelCount; #if (NET_35 || NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47) && !PLATFORM_COMPACTFRAMEWORK @@ -190,11 +191,11 @@ #if INTEROP_VIRTUAL_TABLE DisposeModules(); #endif - Close(false); /* Disposing, cannot throw. */ + Close(true); /* Disposing, cannot throw. */ } } finally { base.Dispose(disposing); @@ -243,11 +244,11 @@ // It isn't necessary to cleanup any functions we've registered. If the connection // goes to the pool and is resurrected later, re-registered functions will overwrite the // previous functions. The SQLiteFunctionCookieHandle will take care of freeing unmanaged // resources belonging to the previously-registered functions. - internal override void Close(bool canThrow) + internal override void Close(bool disposing) { if (_sql != null) { if (!_sql.OwnHandle) { @@ -255,14 +256,17 @@ return; } bool unbindFunctions = ((_flags & SQLiteConnectionFlags.UnbindFunctionsOnClose) == SQLiteConnectionFlags.UnbindFunctionsOnClose); + + retry: if (_usePool) { - if (SQLiteBase.ResetConnection(_sql, _sql, canThrow)) + if (SQLiteBase.ResetConnection(_sql, _sql, !disposing) && + UnhookNativeCallbacks(true, !disposing)) { if (unbindFunctions) { if (SQLiteFunction.UnbindAllFunctions(this, _flags, false)) { @@ -291,31 +295,42 @@ SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); SQLiteConnection.OnChanged(null, new ConnectionEventArgs( SQLiteConnectionEventType.ClosedToPool, null, null, null, null, _sql, _fileName, new object[] { - typeof(SQLite3), canThrow, _fileName, _poolVersion })); + typeof(SQLite3), !disposing, _fileName, _poolVersion })); #if !NET_COMPACT_20 && TRACE_CONNECTION Trace.WriteLine(HelperMethods.StringFormat( CultureInfo.CurrentCulture, "Close (Pool) Success: {0}", HandleToString())); #endif } -#if !NET_COMPACT_20 && TRACE_CONNECTION else { +#if !NET_COMPACT_20 && TRACE_CONNECTION Trace.WriteLine(HelperMethods.StringFormat( CultureInfo.CurrentCulture, "Close (Pool) Failure: {0}", HandleToString())); +#endif + + // + // NOTE: This connection cannot be added to the pool; + // therefore, just use the normal disposal + // procedure on it. + // + _usePool = false; + goto retry; } -#endif } else { + /* IGNORED */ + UnhookNativeCallbacks(disposing, !disposing); + if (unbindFunctions) { if (SQLiteFunction.UnbindAllFunctions(this, _flags, false)) { #if !NET_COMPACT_20 && TRACE_CONNECTION @@ -945,11 +960,11 @@ // NOTE: If the database connection is currently open, attempt to // close it now. This must be done because the file name or // other parameters that may impact the underlying database // connection may have changed. // - if (_sql != null) Close(true); + if (_sql != null) Close(false); // // NOTE: If the connection was not closed successfully, throw an // exception now. // @@ -2942,12 +2957,344 @@ internal override SQLiteErrorCode SetLogCallback(SQLiteLogCallback func) { SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_log( SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, IntPtr.Zero); + if (rc == SQLiteErrorCode.Ok) + _setLogCallback = (func != null); + return rc; } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Appends an error message and an appropriate line-ending to a + /// instance. This is useful because the .NET Compact Framework has a slightly different set + /// of supported methods for the class. + /// + /// + /// The instance to append to. + /// + /// + /// The message to append. It will be followed by an appropriate line-ending. + /// + private static void AppendError( + StringBuilder builder, + string message + ) + { + if (builder == null) + return; + +#if !PLATFORM_COMPACTFRAMEWORK + builder.AppendLine(message); +#else + builder.Append(message); + builder.Append("\r\n"); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// This method attempts to cause the SQLite native library to invalidate + /// its function pointers that refer to this instance. This is necessary + /// to prevent calls from native code into delegates that may have been + /// garbage collected. Normally, these types of issues can only arise for + /// connections that are added to the pool; howver, it is good practice to + /// unconditionally invalidate function pointers that may refer to objects + /// being disposed. + /// + /// Non-zero to also invalidate global function pointers (i.e. those that + /// are not directly associated with this connection on the native side). + /// + /// + /// Non-zero if this method is being executed within a context where it can + /// throw an exception in the event of failure; otherwise, zero. + /// + /// + /// + /// Non-zero if this method was successful; otherwise, zero. + /// + private bool UnhookNativeCallbacks( + bool includeGlobal, + bool canThrow + ) + { + // + // NOTE: Initially, this method assumes success. Then, if any attempt + // to invalidate a function pointer fails, the overall result is + // set to failure. However, this will not prevent further + // attempts, if any, to invalidate subsequent function pointers. + // + bool result = true; + SQLiteErrorCode rc = SQLiteErrorCode.Ok; + StringBuilder builder = new StringBuilder(); + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Rollback Hook (Per-Connection) + try + { + SetRollbackHook(null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset rollback hook: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset rollback hook"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Trace Callback (Per-Connection) + try + { + SetTraceCallback(null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset trace callback: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset trace callback"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Commit Hook (Per-Connection) + try + { + SetCommitHook(null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset commit hook: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset commit hook"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Update Hook (Per-Connection) + try + { + SetUpdateHook(null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset update hook: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset update hook"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Authorizer Hook (Per-Connection) + try + { + SetAuthorizerHook(null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset authorizer hook: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset authorizer hook"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Progress Hook (Per-Connection) + try + { + SetProgressHook(0, null); /* throw */ + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset progress hook: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset progress hook"); + rc = SQLiteErrorCode.Error; + + result = false; + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + #region Log Callback (Global) + // + // NOTE: We have to be careful here because the log callback + // is not per-connection on the native side. It should + // only be unset by this method if this instance was + // responsible for setting it. + // + if (includeGlobal && _setLogCallback) + { + try + { + SQLiteErrorCode rc2 = SetLogCallback(null); /* throw */ + + if (rc2 != SQLiteErrorCode.Ok) + { + AppendError(builder, "could not unset log callback"); + rc = rc2; + + result = false; + } + } +#if !NET_COMPACT_20 && TRACE_CONNECTION + catch (Exception e) +#else + catch (Exception) +#endif + { +#if !NET_COMPACT_20 && TRACE_CONNECTION + try + { + Trace.WriteLine(HelperMethods.StringFormat( + CultureInfo.CurrentCulture, + "Failed to unset log callback: {0}", + e)); /* throw */ + } + catch + { + // do nothing. + } +#endif + + AppendError(builder, "failed to unset log callback"); + rc = SQLiteErrorCode.Error; + + result = false; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////// + + if (!result && canThrow) + throw new SQLiteException(rc, builder.ToString()); + + return result; + } /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Creates a new SQLite backup object based on the provided destination Index: System.Data.SQLite/SQLite3_UTF16.cs ================================================================== --- System.Data.SQLite/SQLite3_UTF16.cs +++ System.Data.SQLite/SQLite3_UTF16.cs @@ -136,11 +136,11 @@ // NOTE: If the database connection is currently open, attempt to // close it now. This must be done because the file name or // other parameters that may impact the underlying database // connection may have changed. // - if (_sql != null) Close(true); + if (_sql != null) Close(false); // // NOTE: If the connection was not closed successfully, throw an // exception now. // Index: System.Data.SQLite/SQLiteBase.cs ================================================================== --- System.Data.SQLite/SQLiteBase.cs +++ System.Data.SQLite/SQLiteBase.cs @@ -127,12 +127,12 @@ /// /// /// After the database has been closed implemeters should call SQLiteFunction.UnbindFunctions() to deallocate all interop allocated /// memory associated with the user-defined functions and collating sequences tied to the closed connection. /// - /// Non-zero if the operation is allowed to throw exceptions, zero otherwise. - internal abstract void Close(bool canThrow); + /// Non-zero if connection is being disposed, zero otherwise. + internal abstract void Close(bool disposing); /// /// Sets the busy timeout on the connection. SQLiteCommand will call this before executing any command. /// /// The number of milliseconds to wait before returning SQLITE_BUSY internal abstract void SetTimeout(int nTimeoutMS); Index: System.Data.SQLite/SQLiteConnection.cs ================================================================== --- System.Data.SQLite/SQLiteConnection.cs +++ System.Data.SQLite/SQLiteConnection.cs @@ -2919,11 +2919,11 @@ _enlistment = null; } #endif if (_sql != null) { - _sql.Close(!_disposing); + _sql.Close(_disposing); _sql = null; } _transactionLevel = 0; _transactionSequence = 0; } @@ -4845,11 +4845,11 @@ CheckDisposed(); if (_sql == null) throw new InvalidOperationException("Database connection not valid for shutdown."); - _sql.Close(true); /* NOTE: MUST be closed before shutdown. */ + _sql.Close(false); /* NOTE: MUST be closed before shutdown. */ SQLiteErrorCode rc = _sql.Shutdown(); #if !NET_COMPACT_20 && TRACE_CONNECTION if (rc != SQLiteErrorCode.Ok) System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat( ADDED Tests/tkt-0e48e80333.eagle Index: Tests/tkt-0e48e80333.eagle ================================================================== --- /dev/null +++ Tests/tkt-0e48e80333.eagle @@ -0,0 +1,298 @@ +############################################################################### +# +# tkt-0e48e80333.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-0e48e80333-1.1 {unhook delegates on pooled close} -setup { + moveEagleShellMdaConfig false + saveMdaConfigEnvironment +} -body { + set configFileName [writeEagleShellMdaConfig [string trim { + + + + + + + }]] + + set scriptFileName [file tempname] + + writeFile $scriptFileName [string trim { + package require Eagle + package require Eagle.Library + package require Eagle.Test + package require System.Data.SQLite.Test + + proc traceCallback { sender e } { + lappend ::result [$e Statement] + lappend ::result [$sender IsReadOnly null] + } + + object load System.Data.SQLite + set ::test_channel stdout + + setupDb [set fileName tkt-0e48e80333-1.1.db] + sql execute $db {CREATE TABLE t1(x);} + cleanupDb $fileName db true false false + + set ::result "" + + set count 10 + + set sql [string trim { \ + INSERT INTO t1 (x) VALUES(1); \ + }] + + for {set i 0} {$i < $count} {incr i} { + setupDb $fileName \ + "" "" "" "" "Pooling=True;" false false + + set connection [getDbConnection] + $connection add_Trace traceCallback + + sql execute $db $sql + + freeDbConnection + cleanupDb $fileName db true false false + } + + if {$::result eq [lrepeat $count $sql False]} then { + exit Success + } else { + exit Failure + } + }] + + set env(COMPLUS_MDA) 1; # enable MDA config file. + + set code [catch { + execTestShell [list \ + -eventflags Wait -success Success -stdout output] \ + -preInitialize [appendArgs \" "set no(logFileName) 1" \"] \ + -file [appendArgs \" $scriptFileName \"] \ + -logFile [appendArgs \" [getTestLog] \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + set code +} -cleanup { + catch {file delete $scriptFileName} + catch {file delete $configFileName} + + unset -nocomplain code output error scriptFileName configFileName + + restoreMdaConfigEnvironment + moveEagleShellMdaConfig true +} -constraints {eagle dotNet testExec command.object monoBug28 command.sql\ +compile.DATA SQLite System.Data.SQLite} -result {0}} + +############################################################################### + +runTest {test tkt-0e48e80333-1.2 {delegate MDA on pooled close} -setup { + moveEagleShellMdaConfig false + saveEnvironmentVariables [list checkForVendorQuiet] + saveMdaConfigEnvironment + + setupDb [set fileName tkt-0e48e80333-1.2.db] +} -body { + set configFileName [writeEagleShellMdaConfig [string trim { + + + + + + + }]] + + set scriptFileName [file tempname] + + writeFile $scriptFileName [string trim { + package require Eagle + package require Eagle.Library + package require Eagle.Test + package require System.Data.SQLite.Test + + set ::test_channel ""; # disable [tputs] + + set id [object invoke Interpreter.GetActive NextId] + set dataSource [file join [getDatabaseDirectory] $fileName] + + set count(1) 1000; # thread work-item count + set count(2) 1000; # per-thread query count + set count(3) [expr {$count(1) * $count(2)}]; # total query count + set count(4) [expr {0.5 * $count(3)}]; # timeout in seconds + set count(5) 10; # busy loop sleep milliseconds + + set sql { \ + SELECT 1; \ + } + + unset -nocomplain results errors + + set code [compileCSharpWith [subst { + using System; + using System.Data; + using System.Data.SQLite; + using System.Threading; + + namespace _Dynamic${id} + { + public static class Test${id} + { + #region Private Static Data + private static long count; + + ///////////////////////////////////////////////////////////////////// + + private static SQLiteTraceEventHandler handler; + #endregion + + ///////////////////////////////////////////////////////////////////// + + #region Public Static Methods + public static void TraceEventHandler( + object sender, + TraceEventArgs e + ) + { + /* IGNORED */ + Interlocked.Increment(ref count); + } + + ///////////////////////////////////////////////////////////////////// + + public static SQLiteConnection MakeConnection() + { + SQLiteConnection connection = new SQLiteConnection( + "Data Source=${dataSource};Journal Mode=Wal;Pooling=true;" + + "[getFlagsProperty AllowNestedTransactions]"); + + connection.Open(); + connection.Trace += handler; + + return connection; + } + + ///////////////////////////////////////////////////////////////////// + + public static long ThreadedPoolTraceTest() + { + for (int index1 = 0; index1 < ${count(1)}; index1++) + { + ThreadPool.QueueUserWorkItem(delegate(object state) { + using (SQLiteConnection connection = MakeConnection()) + { + for (int index2 = 0; index2 < ${count(2)}; index2++) + { + using (SQLiteTransaction transaction = + connection.BeginTransaction()) + { + using (SQLiteCommand command = + connection.CreateCommand()) + { + command.CommandText = "[subst ${sql}]"; + command.ExecuteNonQuery(); + } + } + } + } + }); + + GC.Collect(); + } + + DateTime start = DateTime.UtcNow; + + while (true) + { + if (Interlocked.CompareExchange(ref count, 0, 0) >= ${count(3)}) + break; + + if (DateTime.UtcNow.Subtract(start).TotalSeconds >= ${count(4)}) + break; + + Thread.Sleep(${count(5)}); + } + + return Interlocked.CompareExchange(ref count, 0, 0); + } + + ///////////////////////////////////////////////////////////////////// + + public static void Main() + { + handler = new SQLiteTraceEventHandler(TraceEventHandler); + } + #endregion + } + } + }] true true true results errors System.Data.SQLite.dll] + + puts stdout [list $code $results \ + [expr {[info exists errors] ? $errors : ""}] \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} Main + } result] : [set result ""]}] $result \ + [expr {$code eq "Ok" ? [catch { + object invoke _Dynamic${id}.Test${id} ThreadedPoolTraceTest + } result] : [set result ""]}] $result] + }] + + set env(checkForVendorQuiet) 1 + set env(COMPLUS_MDA) 1; # enable MDA config file. + + set code [catch { + execTestShell [list \ + -eventflags Wait -success Success -stdout output] \ + -preInitialize [appendArgs \" "set fileName {" $fileName }\"] \ + -preInitialize [appendArgs \" "set no(logFileName) 1" \"] \ + -file [appendArgs \" $scriptFileName \"] \ + -logFile [appendArgs \" [getTestLog] \"] + } error] + + tlog "---- BEGIN STDOUT OUTPUT\n" + tlog $output + tlog "\n---- END STDOUT OUTPUT\n" + + list $code [string trim $error] +} -cleanup { + cleanupDb $fileName + + catch {file delete $scriptFileName} + catch {file delete $configFileName} + + unset -nocomplain code output error scriptFileName configFileName + unset -nocomplain db fileName + + restoreMdaConfigEnvironment + restoreEnvironmentVariables [list checkForVendorQuiet] + moveEagleShellMdaConfig true +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\ +System.Data.SQLite compileCSharp} -match regexp -result {^0 \{Ok\ +System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\} 0 1000\}$}} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue Index: Tests/tkt-d4728aecb7.eagle ================================================================== --- Tests/tkt-d4728aecb7.eagle +++ Tests/tkt-d4728aecb7.eagle @@ -80,14 +80,11 @@ package require Eagle.Test package require System.Data.SQLite.Test moveSystemDataSQLiteDllConfig false - set fileName [file join \ - [getBinaryDirectory] System.Data.SQLite.dll.config] - - writeFile $fileName [string trim { + set fileName [writeSystemDataSQLiteDllConfig [string trim { @@ -95,11 +92,11 @@ value="prfx2/%PreLoadSQLite_TargetFramework%/sufx2" /> - }] + }]] object load -loadtype Bytes [base64 encode [readFile [file join \ [getBinaryDirectory] System.Data.SQLite.dll]]] set result [list] Index: lib/System.Data.SQLite/common.eagle ================================================================== --- lib/System.Data.SQLite/common.eagle +++ lib/System.Data.SQLite/common.eagle @@ -1198,10 +1198,115 @@ "\", it does not exist\n"] } } } } + + proc writeSystemDataSQLiteDllConfig { data {verbose true} } { + set directory [getBinaryDirectory] + + if {[string length $directory] == 0} then { + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- skipped moving \"System.Data.SQLite.dll.config\", " \ + "no binary directory\n"] + } + + return + } + + set fileName [file normalize \ + [file join $directory System.Data.SQLite.dll.config]] + + writeFile $fileName $data + + if {$verbose} then { + tputs $::test_channel \ + "---- wrote \"System.Data.SQLite.dll.config\"\n" + } + + return $fileName + } + + proc moveEagleShellMdaConfig { {restore false} {verbose true} } { + set directory [getBinaryDirectory] + + if {[string length $directory] == 0} then { + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- skipped moving \"EagleShell.exe.mda.config\", " \ + "no binary directory\n"] + } + + return + } + + set fileName(1) [file normalize \ + [file join $directory EagleShell.exe.mda.config]] + + set fileName(2) [appendArgs $fileName(1) .moved] + + if {$restore} then { + if {[file exists $fileName(2)]} then { + file rename $fileName(2) $fileName(1) + + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- moved \"" $fileName(2) "\" to \"" \ + $fileName(1) \"\n] + } + } else { + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- skipped moving \"" $fileName(2) \ + "\", it does not exist\n"] + } + } + } else { + if {[file exists $fileName(1)]} then { + file rename $fileName(1) $fileName(2) + + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- moved \"" $fileName(1) "\" to \"" \ + $fileName(2) \"\n] + } + } else { + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- skipped moving \"" $fileName(1) \ + "\", it does not exist\n"] + } + } + } + } + + proc writeEagleShellMdaConfig { data {verbose true} } { + set directory [getBinaryDirectory] + + if {[string length $directory] == 0} then { + if {$verbose} then { + tputs $::test_channel [appendArgs \ + "---- skipped moving \"EagleShell.exe.mda.config\", " \ + "no binary directory\n"] + } + + return + } + + set fileName [file normalize \ + [file join $directory EagleShell.exe.mda.config]] + + writeFile $fileName $data + + if {$verbose} then { + tputs $::test_channel \ + "---- wrote \"EagleShell.exe.mda.config\"\n" + } + + return $fileName + } proc getAppDomainPreamble { {prefix ""} {suffix ""} } { # # NOTE: This procedure returns a test setup script fragment suitable for # evaluation by an interpreter created in an isolated application @@ -2931,10 +3036,22 @@ # if {[array size savedEnv] == 0} then { unset -nocomplain savedEnv } } + + proc saveMdaConfigEnvironment {} { + upvar 1 savedEnv savedEnv + + saveEnvironmentVariables [list COMPLUS_MDA] savedEnv + } + + proc restoreMdaConfigEnvironment {} { + upvar 1 savedEnv savedEnv + + restoreEnvironmentVariables [list COMPLUS_MDA] savedEnv + } proc saveGetSettingValueEnvironment {} { upvar 1 savedEnv savedEnv saveEnvironmentVariables [list \