#if DEBUG #define DEBUG_BINARY_COMPARISON #endif using SqliteDotNet; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Xml; namespace OSGeo.MapGuide.Test.Common { /// /// Extension methods for SqliteVm /// public static class SqliteDbExtensions { public static int Prepare(this SqliteVm vm, string sql, params object[] args) { string formattedSql = string.Format(sql, args); return vm.Prepare(formattedSql); } public static int Execute(this SqliteVm vm, string sql, params object[] args) { string formattedSql = string.Format(sql, args); return vm.Execute(formattedSql); } public static bool ReadParameterValue(this SqliteVm vm, int paramSetId, string paramName, NameValueCollection result, bool bIsPath = false) { int stat = vm.Execute("Select ParamValue from Params WHERE ParamSet={0} AND ParamName=\"{1}\"", paramSetId, paramName); if (stat == Sqlite.Row) { string str = vm.GetString("ParamValue"); if (bIsPath) { str = CommonUtility.GetPath(str); } result.Add(paramName, str); return true; } return false; } public static bool ReadParameterValue(this SqliteVm vm, string paramName, NameValueCollection result) { int stat = vm.Execute("Select ParamValue from Params WHERE ParamName=\"{0}\"", paramName); if (stat == Sqlite.Row) { result.Add(paramName, vm.GetString("ParamValue")); return true; } return false; } public static bool ReadCommonParameterValue(this SqliteVm vm, string paramName, NameValueCollection result) { int stat = vm.Execute("Select ParamValue from CommonParams WHERE ParamName=\"{0}\"", paramName); if (stat == Sqlite.Row) { result.Add(paramName, vm.GetString("ParamValue")); return true; } return false; } } /// /// Common utility methods /// public class CommonUtility { public static NameValueCollection SetCommonParams(int paramSet, SqliteDb db) { NameValueCollection result = null; var vm = new SqliteVm(db, false); try { result = new NameValueCollection(); vm.ReadParameterValue(paramSet, "OPERATION", result); vm.ReadCommonParameterValue("VERSION", result); vm.ReadCommonParameterValue("CREDENTIALS", result); vm.ReadCommonParameterValue("LOCALE", result); vm = null; } catch (MgException ex) { throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails())); } catch (Exception ex) { try { vm = null; vm = new SqliteVm(db, true); vm.ReadParameterValue("VERSION", result); vm.ReadParameterValue("CREDENTIALS", result); vm.ReadParameterValue("LOCALE", result); } catch (Exception ex2) { } } return result; } public static MgByteReader GetByteReaderFromPath(string path, bool bCheck = true) { if (bCheck) { if (File.Exists(path)) { MgByteSource source = new MgByteSource(path); MgByteReader reader = source.GetReader(); return reader; } return null; } else { MgByteSource source = new MgByteSource(path); MgByteReader reader = source.GetReader(); return reader; } } public static string GetDbPath(string dumpFileName) { var db = new SqliteDb(); var dbPath = dumpFileName.Replace(".dump", ".db"); var dbName = CommonUtility.GetPath(dbPath); if (!File.Exists(dumpFileName) && !File.Exists(dbName)) { throw new UnitTestException(string.Format("Error: Dump file {0} not found. Unable to create database file", dumpFileName)); } else if (!File.Exists(dbName)) { db.GenerateDatabase(dumpFileName, dbName); } else if (File.Exists(dumpFileName) && File.GetLastWriteTimeUtc(dumpFileName) > File.GetLastWriteTimeUtc(dbName)) { try { File.Delete(dbName); db.GenerateDatabase(dumpFileName, dbName); } catch { throw new UnitTestException(string.Format("Unable to delete database file {0}. The file is either in use or is read-only. The database has not been updated", dbName)); } } return dbPath; } public static string GetPath(string dbPath) { if (Path.IsPathRooted(dbPath)) return dbPath.Replace("\\", "/"); else return Path.Combine(GetAssemblyPath(), dbPath).Replace("\\", "/"); } private static string GetAssemblyPath() { return Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath); } public static MgStringCollection StringToMgStringCollection(string str) { try { MgStringCollection coll = new MgStringCollection(); string[] tokens = str.Split(','); foreach (var token in tokens) { coll.Add(token); } return coll; } catch (MgException ex) { throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails())); } } public static string MgStringCollectionToString(MgStringCollection coll) { try { //Sigh, we're too smart for our own good. Yes, the trailing comma //should be there! StringBuilder sb = new StringBuilder(); for (int i = 0; i < coll.GetCount(); i++) { sb.Append(coll.GetItem(i)); sb.Append(","); } return sb.ToString(); /* List items = new List(); for (int i = 0; i < coll.GetCount(); i++) { items.Add(coll.GetItem(i)); } return string.Join(",", items.ToArray()); */ } catch (MgException ex) { throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails())); } } public static string BooleanToString(bool b) { return b ? "True" : "False"; } public static string MgEnvelopeToString(MgEnvelope env) { try { var ll = env.GetLowerLeftCoordinate(); var ur = env.GetUpperRightCoordinate(); return string.Format(CultureInfo.InvariantCulture, "({0}:{1})-({2}:{3})", ll.GetX(), ll.GetY(), ur.GetX(), ur.GetY()); } catch (MgException ex) { throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails())); } } public static string MgPointToString(MgPoint pt) { try { var coord = pt.GetCoordinate(); return string.Format(CultureInfo.InvariantCulture, "({0}:{1})", coord.GetX(), coord.GetY()); } catch (MgException ex) { throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails())); } } public static string GetExtension(string name) { if (name.LastIndexOf(".") >= 0) { return name.Substring(name.LastIndexOf(".") + 1); } else { if (name == "MG_USER_CREDENTIALS") return "txt"; else return "bin"; } } public static string GetMimeType(string extension) { switch (extension) { case "agf": return "application/agf"; case "bin": return "application/octet-stream"; case "dwf": return "model/vnd.dwf"; case "jpg": case "jpeg": return "image/jpeg"; case "png": return "image/png"; case "tif": case "tiff": return "image/tiff"; case "html": return "text/html"; case "txt": return "text/plain"; case "xml": return "text/xml"; default: return "application/octet-stream"; } } public static object SpecialDataHandling(string operation, object resultData, string mimeType) { object res = resultData; switch (operation) { case "ENUMERATERESOURCES": res = RemoveTimeStamp(resultData.ToString()); break; case "GETDRAWINGLAYER": res = RemoveDwfSectionName(resultData, Encoding.UTF8); break; case "GETDRAWINGSECTION": res = RemoveDwfSectionName(resultData, Encoding.UTF8); break; case "GETLOG": res = RemoveLogEntryTimeStamp(resultData.ToString()); break; case "GETMAP": res = GetMapHeader(resultData.ToString()); break; case "GETLONGTRANSACTIONS": res = RemoveCreationDate(resultData.ToString()); break; } string strRes = res as string; if (strRes != null && mimeType == "text/xml") { var doc = new XmlDocument(); doc.LoadXml(strRes); res = SortElement(doc, ""); } return res; } class InvertedComparer : IComparer { private IComparer _comp; public InvertedComparer(IComparer comp) { _comp = comp; } public int Compare(T x, T y) { int res = _comp.Compare(x, y); //Invert the non-zero results if (res > 0) return -1; else if (res < 0) return 1; else return 0; } } private static string SortElement(XmlNode elem, string preText) { var elemArray = new List(); string elemString = ""; if (elem.ChildNodes.Count > 0) { int elCount = 0; int txtCount = 0; //foreach (XmlNode child in elem.ChildNodes) for (int i = 0; i < elem.ChildNodes.Count; i++) { var child = elem.ChildNodes[i]; if (child.NodeType == XmlNodeType.Element) { var elemValue = SortElement(child, preText + " "); if (!string.IsNullOrEmpty(elemValue)) { elemArray.Add(elemValue); elCount++; } } else if (child.NodeType == XmlNodeType.Text) { string content = child.InnerText.Trim(); if (!string.IsNullOrEmpty(content)) { elemArray.Add(content); txtCount++; } } } //We have to ordinal compare to match the sort behaviour of //sort() in PHP elemArray.Sort((s1, s2) => { return string.CompareOrdinal(s1, s2); }); foreach (string str in elemArray) { elemString += str; } } string endTag = ""; if (elemArray.Count > 1 && elemString.Length > 0) { endTag = "\n" + preText; } string tagName = ""; if (!(elem is XmlDocument)) { tagName = elem.Name; } endTag += ""; if ("" != tagName) { elemString = "\n" + preText + "<" + tagName + ">" + elemString + endTag; } return elemString; } private static string RemoveTimeStamp(string resultData) { string result = resultData; string newResult = result; while (result.IndexOf("") >= 0) { newResult = result.Substring(0, result.IndexOf("")); newResult += result.Substring(result.IndexOf("") + "".Length); result = newResult; } return newResult; } private static object RemoveDwfSectionName(object resultData, Encoding enc) { bool bFromByteArray = false; byte[] bResultData = resultData as byte[]; string strResultData = resultData as string; /* if (strResultData == null) { if (bResultData != null) { strResultData = enc.GetString(bResultData); bFromByteArray = true; } }*/ if (strResultData != null) { //Console.WriteLine("RemoveDwfSectionName: length = {0}", strResultData.Length); int idx = strResultData.IndexOf(".w2d"); //Console.WriteLine("RemoveDwfSectionName: widx = {0}", idx); if (idx >= 0) { string newResult = strResultData.Substring(idx); int eidx = newResult.IndexOf("EndOfDWF"); //Console.WriteLine("RemoveDwfSectionName: eidx = {0}", eidx); if (0 != eidx) { newResult = newResult.Substring(0, eidx); //Console.WriteLine("RemoveDwfSectionName: newlength = {0}", newResult.Length); } if (bFromByteArray) return enc.GetBytes(newResult); else return newResult; } } else if (bResultData != null) { byte[] bW2d = enc.GetBytes(".w2d"); byte[] bEOF = enc.GetBytes("EndOfDWF"); int widx = -1; int eidx = -1; int wMatches = 0; int eMatches = 0; int i = 0; while(i < bResultData.Length) { //Haven't found .w2d sequence if (widx < 0 && wMatches == 0) { //We've found a "." if (bResultData[i] == bW2d[0]) { wMatches++; i++; //Now try to follow through this sequence to see if it is ".w2d" while (wMatches < bW2d.Length) { //End of array. Abort if (i >= bResultData.Length) break; //Next byte in sequence matches. Advance if (bResultData[i] == bW2d[wMatches]) { //Increment matches wMatches++; //Check if full sequence matches if (wMatches == bW2d.Length) { //Match. Record index which is current position minus the # of consecutive matches widx = i - wMatches; break; } } else //Incomplete sequence. Break this loop { wMatches = 0; //Reset break; } } } } //Haven't found EndOfDWF sequence else if (eidx < 0 && eMatches == 0) { //We've found a "E" if (bResultData[i] == bEOF[0]) { eMatches++; i++; //Now try to follow through this sequence to see if it is "EndOfDWF" while (eMatches < bEOF.Length) { //End of array. Abort if (i >= bResultData.Length) break; //Next byte in sequence matches. Advance if (bResultData[i] == bEOF[eMatches]) { //Increment matches eMatches++; //Check if full sequence matches if (eMatches == bEOF.Length) { //Match. Record index which is current position minus the # of consecutive matches eidx = i - eMatches; break; } } else //Incomplete sequence. Break this loop { eMatches = 0; //Reset break; } } } } //Found both offsets. We're done if (widx > 0 && eidx > widx) break; i++; } if (widx > 0 && eidx > widx) { byte[] newResult = new byte[eidx - widx]; int off = 0; for (int j = widx; j <= eidx; j++) { newResult[off] = bResultData[j]; } } } return resultData; } private static string RemoveLogEntryTimeStamp(string resultData) { string result = resultData; string newResult = result; while (result.IndexOf("<") >= 0) { newResult = result.Substring(0, result.IndexOf("<")); newResult += result.Substring(result.IndexOf(">") + 1); result = newResult; } return newResult; } private static string GetMapHeader(string resultData) { if (resultData.IndexOf("(DWF V06.01)") >= 0) resultData = "(DWF V06.01)"; return resultData; } private static string RemoveCreationDate(string resultData) { string newResult = resultData; while (resultData.IndexOf("") >= 0) { newResult = resultData.Substring(0, resultData.IndexOf("")); newResult += resultData.Substring(resultData.IndexOf("") + "".Length); resultData = newResult; } return newResult; } public static object ProcessExceptionMessage(object resultData) { string strResultData = resultData as string; if (strResultData != null) { string text = "exception occurred"; if (strResultData.Contains(text)) { strResultData = strResultData.Substring(0, strResultData.IndexOf(text) + text.Length); } return strResultData; } return resultData; } public static bool SpecialValidation(string operation, object resultData, object expectedResult) { if (operation == "GETFEATUREPROVIDERS") { //We expect both to be strings here return GetFeatureProvidersValidation(resultData.ToString(), expectedResult.ToString()); } return false; } private static bool GetFeatureProvidersValidation(string resultData, string expectedResult) { throw new NotImplementedException(); } public static object RemoveStackTraceFromResult(object result) { var strResult = result as string; //TODO: Clean out stack trace return result; } public static string GetExtensionFromMimeType(string mimeType) { string extension = "xml"; if (mimeType.Contains("ePlot")) return "dwf"; if (mimeType.Contains("text/plain")) return "txt"; if (mimeType.Contains("text/html")) return "html"; switch (mimeType) { case "application/agf": return "agf"; case "application/octet-stream": return "bin"; case "model/vnd.dwf": return "dwf"; case "image/jpeg": return "jpg"; case "image/png": return "png"; case "image/tiff": return "tiff"; case "application/x-w2d": return "dwf"; } return extension; } public static bool ByteArraysEqual(byte[] bExpected, byte[] bActual, string operation, string testName) { if (bExpected == null && bActual != null) return false; if (bExpected != null && bActual == null) return false; bool bRet = true; #if DEBUG_BINARY_COMPARISON bool bLogged = false; Guid guid = Guid.NewGuid(); using (StreamWriter sw1 = new StreamWriter(guid.ToString() + "_" + operation + "_" + testName + "_expected.txt", false)) using (StreamWriter sw2 = new StreamWriter(guid.ToString() + "_" + operation + "_" + testName + "_actual.txt", false)) { #endif for (int i = 0; i < bExpected.Length; i++) { if (i >= bExpected.Length || i >= bActual.Length) { break; } byte b1 = bExpected[i]; byte b2 = bActual[i]; #if DEBUG_BINARY_COMPARISON sw1.WriteLine("{0} {1}", b1, Convert.ToChar(b1)); sw2.WriteLine("{0} {1}", b2, Convert.ToChar(b2)); #endif if (b1 != b2) { #if DEBUG_BINARY_COMPARISON bRet = false; if (!bLogged) { System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: Comparison {0} returned false. See logged text files", guid.ToString())); bLogged = true; } #else return false; #endif } } #if DEBUG_BINARY_COMPARISON } if (bRet) { File.Delete(guid.ToString() + "_" + operation + "_" + testName + "_expected.txt"); File.Delete(guid.ToString() + "_" + operation + "_" + testName + "_actual.txt"); } #endif System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: {0} - {1} - COMPARE: {2} with {3} = {4}", testName, operation, bExpected.Length, bActual.Length, (bRet ? 0 : 1))); return bRet; } } }