using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Tests.TestHelpers { /// /// Common helper properties and methods useful to testing /// public static class TestHelper { /// /// Gets the current assembly directory. /// /// The assembly directory. static public string CurrentAssemblyDirectory { get { var codeBase = typeof(TestHelper).Assembly.CodeBase; var uri = new Uri(codeBase); var path = uri.LocalPath; return Path.GetDirectoryName(path); } } /// /// Maps the given making it rooted on . must start with ~/ /// /// The relative path. /// public static string MapPathForTest(string relativePath) { if (!relativePath.StartsWith("~/")) throw new ArgumentException("relativePath must start with '~/'", "relativePath"); return relativePath.Replace("~/", CurrentAssemblyDirectory + "/"); } public static void InitializeContentDirectories() { CreateDirectories(new[] { SystemDirectories.Masterpages, SystemDirectories.MvcViews, SystemDirectories.Media, SystemDirectories.AppPlugins }); } public static void CleanContentDirectories() { CleanDirectories(new[] { SystemDirectories.Masterpages, SystemDirectories.MvcViews, SystemDirectories.Media }); } public static void CreateDirectories(string[] directories) { foreach (var directory in directories) { var directoryInfo = new DirectoryInfo(IOHelper.MapPath(directory)); if (directoryInfo.Exists == false) Directory.CreateDirectory(IOHelper.MapPath(directory)); } } public static void CleanDirectories(string[] directories) { var preserves = new Dictionary { { SystemDirectories.Masterpages, new[] {"dummy.txt"} }, { SystemDirectories.MvcViews, new[] {"dummy.txt"} } }; foreach (var directory in directories) { var directoryInfo = new DirectoryInfo(IOHelper.MapPath(directory)); var preserve = preserves.ContainsKey(directory) ? preserves[directory] : null; if (directoryInfo.Exists) directoryInfo.GetFiles().Where(x => preserve == null || preserve.Contains(x.Name) == false).ForEach(x => x.Delete()); } } public static void CleanUmbracoSettingsConfig() { var currDir = new DirectoryInfo(CurrentAssemblyDirectory); var umbracoSettingsFile = Path.Combine(currDir.Parent.Parent.FullName, "config", "umbracoSettings.config"); if (File.Exists(umbracoSettingsFile)) File.Delete(umbracoSettingsFile); } public static void AssertAllPropertyValuesAreEquals(object actual, object expected, string dateTimeFormat = null, Func sorter = null, string[] ignoreProperties = null) { var properties = expected.GetType().GetProperties(); foreach (var property in properties) { //ignore properties that are attributed with this var att = property.GetCustomAttribute(false); if (att != null && att.State == EditorBrowsableState.Never) continue; if (ignoreProperties != null && ignoreProperties.Contains(property.Name)) continue; var expectedValue = property.GetValue(expected, null); var actualValue = property.GetValue(actual, null); if (((actualValue is string) == false) && actualValue is IEnumerable) { AssertListsAreEquals(property, (IEnumerable)actualValue, (IEnumerable)expectedValue, dateTimeFormat, sorter); } else if (dateTimeFormat.IsNullOrWhiteSpace() == false && actualValue is DateTime) { // round to second else in some cases tests can fail ;-( var expectedDateTime = (DateTime) expectedValue; expectedDateTime = expectedDateTime.AddTicks(-(expectedDateTime.Ticks%TimeSpan.TicksPerSecond)); var actualDateTime = (DateTime) actualValue; actualDateTime = actualDateTime.AddTicks(-(actualDateTime.Ticks % TimeSpan.TicksPerSecond)); Assert.AreEqual(expectedDateTime.ToString(dateTimeFormat), actualDateTime.ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); } else { Assert.AreEqual(expectedValue, actualValue, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); } } } private static void AssertListsAreEquals(PropertyInfo property, IEnumerable actualList, IEnumerable expectedList, string dateTimeFormat, Func sorter) { if (sorter == null) { //this is pretty hackerific but saves us some code to write sorter = enumerable => { //semi-generic way of ensuring any collection of IEntity are sorted by Ids for comparison var entities = enumerable.OfType().ToList(); if (entities.Count > 0) { return entities.OrderBy(x => x.Id); } else { return enumerable; } }; } var actualListEx = sorter(actualList).Cast().ToList(); var expectedListEx = sorter(expectedList).Cast().ToList(); if (actualListEx.Count != expectedListEx.Count) Assert.Fail("Collection {0}.{1} does not match. Expected IEnumerable containing {2} elements but was IEnumerable containing {3} elements", property.PropertyType.Name, property.Name, expectedListEx.Count, actualListEx.Count); for (int i = 0; i < actualListEx.Count; i++) { var actualValue = actualListEx[i]; var expectedValue = expectedListEx[i]; if (((actualValue is string) == false) && actualValue is IEnumerable) { AssertListsAreEquals(property, (IEnumerable)actualValue, (IEnumerable)expectedValue, dateTimeFormat, sorter); } else if (dateTimeFormat.IsNullOrWhiteSpace() == false && actualValue is DateTime) { Assert.AreEqual(((DateTime)expectedValue).ToString(dateTimeFormat), ((DateTime)actualValue).ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); } else { Assert.AreEqual(expectedValue, actualValue, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); } } } public static void DeleteDirectory(string path) { Try(() => { if (Directory.Exists(path) == false) return; foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) File.Delete(file); }); Try(() => { if (Directory.Exists(path) == false) return; Directory.Delete(path, true); }); } public static void TryAssert(Action action, int maxTries = 5, int waitMilliseconds = 200) { Try(action, maxTries, waitMilliseconds); } public static void Try(Action action, int maxTries = 5, int waitMilliseconds = 200) { Try(action, maxTries, waitMilliseconds); } public static void Try(Action action, int maxTries = 5, int waitMilliseconds = 200) where T : Exception { var tries = 0; while (true) { try { action(); break; } catch (T) { if (tries++ > maxTries) throw; Thread.Sleep(waitMilliseconds); } } } } }