Example Test SQLite Assets
From OpenSimulator
An Example Test - SQLite Assets
using System; using System.IO; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; using OpenSim.Framework; using OpenSim.Data.Tests; using OpenSim.Data.SQLite; using OpenSim.Region.Environment.Scenes; using OpenMetaverse; namespace OpenSim.Data.SQLite.Tests { [TestFixture] public class SQLiteAssetTest { public string file; public string connect; public AssetDataBase db; public UUID uuid1; public UUID uuid2; public UUID uuid3; public string name1; public string name2; public string name3; public byte[] asset1; [TestFixtureSetUp] public void Init() { uuid1 = UUID.Random(); uuid2 = UUID.Random(); uuid3 = UUID.Random(); name1 = "asset one"; name2 = "asset two"; name3 = "asset three"; asset1 = new byte[100]; asset1.Initialize(); file = Path.GetTempFileName() + ".db"; connect = "URI=file:" + file + ",version=3"; db = new SQLiteAssetData(); db.Initialise(connect); } [TestFixtureTearDown] public void Cleanup() { db.Dispose(); System.IO.File.Delete(file); } [Test] public void T001_LoadEmpty() { Assert.That(db.ExistsAsset(uuid1), Is.False); Assert.That(db.ExistsAsset(uuid2), Is.False); Assert.That(db.ExistsAsset(uuid3), Is.False); } [Test] public void T010_StoreSimpleAsset() { AssetBase a1 = new AssetBase(uuid1, name1); AssetBase a2 = new AssetBase(uuid2, name2); AssetBase a3 = new AssetBase(uuid3, name3); a1.Data = asset1; a2.Data = asset1; a3.Data = asset1; db.CreateAsset(a1); db.CreateAsset(a2); db.CreateAsset(a3); AssetBase a1a = db.FetchAsset(uuid1); Assert.That(a1a.ID, Is.EqualTo(uuid1)); Assert.That(a1a.Name, Is.EqualTo(name1)); AssetBase a2a = db.FetchAsset(uuid2); Assert.That(a2a.ID, Is.EqualTo(uuid2)); Assert.That(a2a.Name, Is.EqualTo(name2)); AssetBase a3a = db.FetchAsset(uuid3); Assert.That(a3a.ID, Is.EqualTo(uuid3)); Assert.That(a3a.Name, Is.EqualTo(name3)); } [Test] public void T011_ExistsSimpleAsset() { Assert.That(db.ExistsAsset(uuid1), Is.True); Assert.That(db.ExistsAsset(uuid2), Is.True); Assert.That(db.ExistsAsset(uuid3), Is.True); } } }
You can see 4 of the important annotations here:
- TestFixture - this class is a test suite
- TestFixtureSetup - this code is always run before any of the tests are executed
- TestFixtureTearDown - this code is always run after the tests are done executing, even if they fail.
- Test - this method is a test
This Test is Flawed
There is also a flaw in the tests above, namely the subject under test (the SQLiteAssetData
object db
) is used in the tests as well as the fixture setup. Consider the following scenario: the SQLiteAssetData
has a caching mechanism keeps track of DB rows in memory. When rows are fetched, they are stored into the cache. When new rows are inserted or existing rows updated, the cache is updated, and eventually the cache is synced to disk.
Note that in the test above, all interactions with the database are handled by this SQLiteAssetData
object. First, it's used to create the database and tables:
[TestFixture] public class SQLiteAssetTest { // ... public AssetDataBase db; [TestFixtureSetUp] public void Init() { // ... connect = "URI=file:" + Path.GetTempFileName() + ".db,version=3"; db = new SQLiteAssetData(); db.Initialise(connect); } [TestFixtureTearDown] public void Cleanup() { db.Dispose(); // ... }
Then, in T010_StoreSimpleAsset()
, db.CreateAsset()
is called, with the intent to persist the asset to the database on disk. However to verify whether this operation succeeded, db.FetchAsset()
is called:
[Test] public void T010_StoreSimpleAsset() { // ... db.CreateAsset(a1); db.CreateAsset(a2); db.CreateAsset(a3); AssetBase a1a = db.FetchAsset(uuid1); Assert.That(a1a.ID, Is.EqualTo(uuid1)); Assert.That(a1a.Name, Is.EqualTo(name1)); AssetBase a2a = db.FetchAsset(uuid2); Assert.That(a2a.ID, Is.EqualTo(uuid2)); Assert.That(a2a.Name, Is.EqualTo(name2)); AssetBase a3a = db.FetchAsset(uuid3); Assert.That(a3a.ID, Is.EqualTo(uuid3)); Assert.That(a3a.Name, Is.EqualTo(name3)); } }
Now assume a change introduced a bug that prevent the cache from being flushed to disk ever. The tests above would never discover this bug!
How to Fix the Flaw
Another means of populating the database and verifying the success of the operations performed by SQLiteAssetData
object should be used instead. For example:
[TestFixture] public class SQLiteAssetTest { // ... public string filename, connect; public AssetDataBase db; [TestFixtureSetUp] public void Init() { // ... filename = Path.GetTempFileName() + ".db"; connect = "URI=file:" + filename + ",version=3"; SQLiteDB sqldb = new SQLiteDBAdapter(connect); // ficticious adapter interfacing directly with SQLite database sqldb.createTable(new SQLiteTable(...)); sqldb.executeSQL("INSET INTO assets VALUES(...)"); db = new SQLiteAssetData(); } [TestFixtureTearDown] public void Cleanup() { db.Dispose(); System.IO.File.Delete(filename); // ... }
Similarly, to verify that the SQLiteAssetData
object creates and retrieves assets properly, test T010_StoreSimpleAsset()
might be broken up into two tests:
[Test] public void T010_StoreSimpleAsset() { AssetBase a1, a2, a3; // initialize a1, a2 and a3 db.CreateAsset(a1); db.CreateAsset(a2); db.CreateAsset(a3); SQLiteDB sqldb = new SQLiteDBAdapter(connect); // ficticious adapter interfacing directly with SQLite database AssetBase a1_actual = sqldb.executeSQL("SELECT * FROM assets WHERE uuid={0}", a1.uuid); Assert.Equals(a1_actual.uuid, a1.uuid); Assert.Equals(a1_actual.Name, a1.Name); // etc } [Test] public void T011_FetchSimpleAsset() { AssetBase a1 = new AssetBase(uuid1, name1, ...); SQLiteDB sqldb = new SQLiteDBAdapter(connect); // ficticious adapter interfacing directly with SQLite database AssetBase a1_actual = sqldb.executeSQL("INSERT INTO assets VALUES({0}, ...)", a1.uuid, ...); // etc // ... AssetBase a1a = db.FetchAsset(uuid1); Assert.That(a1a.ID, Is.EqualTo(uuid1)); Assert.That(a1a.Name, Is.EqualTo(name1)); AssetBase a2a = db.FetchAsset(uuid2); Assert.That(a2a.ID, Is.EqualTo(uuid2)); Assert.That(a2a.Name, Is.EqualTo(name2)); AssetBase a3a = db.FetchAsset(uuid3); Assert.That(a3a.ID, Is.EqualTo(uuid3)); Assert.That(a3a.Name, Is.EqualTo(name3)); } }
This way T010_StoreSimpleAsset
tests SQLiteAssetData.CreateAsset()
only and T011_FetchSimpleAsset()
tests SQLiteAssetData.FetchAsset()
only. If there are any problems in either of those methods or all of them, these tests will discover that there is a problem.