/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.IO; using System.Collections.Generic; using log4net.Config; using NUnit.Framework; using NUnit.Framework.Constraints; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Tests.Common; using log4net; using System.Data; using System.Data.Common; using System.Reflection; namespace OpenSim.Data.Tests { /// This is a base class for testing any Data service for any DBMS. /// Requires NUnit 2.5 or better (to support the generics). /// /// /// FIXME: Should extend OpenSimTestCase but compile on mono 2.4.3 currently fails with /// AssetTests`2 : System.MemberAccessException : Cannot create an instance of OpenSim.Data.Tests.AssetTests`2[TConn,TAssetData] because Type.ContainsGenericParameters is true. /// and similar on EstateTests, InventoryTests and RegionTests. /// Runs fine with mono 2.10.8.1, so easiest thing is to wait until min Mono version uplifts. /// /// /// public class BasicDataServiceTest where TConn : DbConnection, new() where TService : class, new() { protected string m_connStr; private TService m_service; private string m_file; // TODO: Is this in the right place here? // Later: apparently it's not, but does it matter here? // protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected ILog m_log; // doesn't matter here that it's not static, init to correct type in instance .ctor public BasicDataServiceTest() : this("") { } public BasicDataServiceTest(string conn) { m_connStr = !String.IsNullOrEmpty(conn) ? conn : DefaultTestConns.Get(typeof(TConn)); m_log = LogManager.GetLogger(this.GetType()); OpenSim.Tests.Common.TestLogging.LogToConsole(); // TODO: Is that right? } /// /// To be overridden in derived classes. Do whatever init with the m_service, like setting the conn string to it. /// You'd probably want to to cast the 'service' to a more specific type and store it in a member var. /// This framework takes care of disposing it, if it's disposable. /// /// The service being tested protected virtual void InitService(object service) { } [TestFixtureSetUp] public void Init() { // Sorry, some SQLite-specific stuff goes here (not a big deal, as its just some file ops) if (typeof(TConn).Name.StartsWith("Sqlite")) { // SQLite doesn't work on power or z linux if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) Assert.Ignore(); if (Util.IsWindows()) Util.LoadArchSpecificWindowsDll("sqlite3.dll"); // for SQLite, if no explicit conn string is specified, use a temp file if (String.IsNullOrEmpty(m_connStr)) { m_file = Path.GetTempFileName() + ".db"; m_connStr = "URI=file:" + m_file + ",version=3"; } } if (String.IsNullOrEmpty(m_connStr)) { string msg = String.Format("Connection string for {0} is not defined, ignoring tests", typeof(TConn).Name); m_log.Warn(msg); Assert.Ignore(msg); } // Try the connection, ignore tests if Open() fails using (TConn conn = new TConn()) { conn.ConnectionString = m_connStr; try { conn.Open(); conn.Close(); } catch { string msg = String.Format("{0} is unable to connect to the database, ignoring tests", typeof(TConn).Name); m_log.Warn(msg); Assert.Ignore(msg); } } // If we manage to connect to the database with the user // and password above it is our test database, and run // these tests. If anything goes wrong, ignore these // tests. try { m_service = new TService(); InitService(m_service); } catch (Exception e) { m_log.Error(e.ToString()); Assert.Ignore(); } } [TestFixtureTearDown] public void Cleanup() { if (m_service != null) { if (m_service is IDisposable) ((IDisposable)m_service).Dispose(); m_service = null; } if (!String.IsNullOrEmpty(m_file) && File.Exists(m_file)) File.Delete(m_file); } protected virtual DbConnection Connect() { DbConnection cnn = new TConn(); cnn.ConnectionString = m_connStr; cnn.Open(); return cnn; } protected virtual void ExecuteSql(string sql) { using (DbConnection dbcon = Connect()) { using (DbCommand cmd = dbcon.CreateCommand()) { cmd.CommandText = sql; cmd.ExecuteNonQuery(); } } } protected delegate bool ProcessRow(IDataReader reader); protected virtual int ExecQuery(string sql, bool bSingleRow, ProcessRow action) { int nRecs = 0; using (DbConnection dbcon = Connect()) { using (DbCommand cmd = dbcon.CreateCommand()) { cmd.CommandText = sql; CommandBehavior cb = bSingleRow ? CommandBehavior.SingleRow : CommandBehavior.Default; using (DbDataReader rdr = cmd.ExecuteReader(cb)) { while (rdr.Read()) { nRecs++; if (!action(rdr)) break; } } } } return nRecs; } /// Drop tables (listed as parameters). There is no "DROP IF EXISTS" syntax common for all /// databases, so we just DROP and ignore an exception. /// /// protected virtual void DropTables(params string[] tables) { foreach (string tbl in tables) { try { ExecuteSql("DROP TABLE " + tbl + ";"); }catch { } } } /// Clear tables listed as parameters (without dropping them). /// /// protected virtual void ResetMigrations(params string[] stores) { string lst = ""; foreach (string store in stores) { string s = "'" + store + "'"; if (lst == "") lst = s; else lst += ", " + s; } string sCond = stores.Length > 1 ? ("in (" + lst + ")") : ("=" + lst); try { ExecuteSql("DELETE FROM migrations where name " + sCond); } catch { } } /// Clear tables listed as parameters (without dropping them). /// /// protected virtual void ClearTables(params string[] tables) { foreach (string tbl in tables) { try { ExecuteSql("DELETE FROM " + tbl + ";"); } catch { } } } } }