2020-12-20 08:36:11 +01:00
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System ;
2020-09-28 15:43:28 +02:00
using System.Collections ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
2020-09-30 14:54:53 +02:00
using Microsoft.AspNetCore.Hosting ;
2020-10-27 10:53:01 +00:00
using Microsoft.Extensions.DependencyInjection ;
2020-12-20 08:36:11 +01:00
using Microsoft.Extensions.Options ;
2020-09-28 15:43:28 +02:00
using Moq ;
using NUnit.Framework ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core ;
using Umbraco.Cms.Core.Cache ;
using Umbraco.Cms.Core.Composing ;
using Umbraco.Cms.Core.Configuration ;
using Umbraco.Cms.Core.Configuration.Models ;
using Umbraco.Cms.Core.Diagnostics ;
using Umbraco.Cms.Core.Hosting ;
using Umbraco.Cms.Core.IO ;
using Umbraco.Cms.Core.Logging ;
using Umbraco.Cms.Core.Mail ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.Entities ;
using Umbraco.Cms.Core.Models.PublishedContent ;
using Umbraco.Cms.Core.Net ;
using Umbraco.Cms.Core.PropertyEditors ;
using Umbraco.Cms.Core.Routing ;
using Umbraco.Cms.Core.Runtime ;
using Umbraco.Cms.Core.Serialization ;
using Umbraco.Cms.Core.Strings ;
2021-02-10 14:45:44 +01:00
using Umbraco.Cms.Tests.Common ;
2021-02-10 11:42:04 +01:00
using Umbraco.Cms.Web.Common.AspNetCore ;
2020-09-28 15:43:28 +02:00
using Umbraco.Core ;
using Umbraco.Core.Persistence ;
2020-12-20 08:36:11 +01:00
using Umbraco.Core.Persistence.SqlSyntax ;
2021-02-09 13:32:34 +01:00
using Umbraco.Extensions ;
2020-12-20 08:36:11 +01:00
using Umbraco.Infrastructure.Persistence.Mappers ;
2021-02-09 10:22:42 +01:00
using Constants = Umbraco . Cms . Core . Constants ;
2020-09-28 15:43:28 +02:00
using File = System . IO . File ;
2021-02-09 10:22:42 +01:00
using IHostingEnvironment = Umbraco . Cms . Core . Hosting . IHostingEnvironment ;
2020-09-28 15:43:28 +02:00
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Common helper properties and methods useful to testing
/// </summary>
public static class TestHelper
{
2020-12-20 08:36:11 +01:00
private static readonly TestHelperInternal s_testHelperInternal = new TestHelperInternal ( ) ;
2020-09-28 15:43:28 +02:00
private class TestHelperInternal : TestHelperBase
{
2020-12-20 08:36:11 +01:00
public TestHelperInternal ( )
: base ( typeof ( TestHelperInternal ) . Assembly )
2020-09-28 15:43:28 +02:00
{
}
public override IDbProviderFactoryCreator DbProviderFactoryCreator { get ; } = Mock . Of < IDbProviderFactoryCreator > ( ) ;
public override IBulkSqlInsertProvider BulkSqlInsertProvider { get ; } = Mock . Of < IBulkSqlInsertProvider > ( ) ;
public override IMarchal Marchal { get ; } = Mock . Of < IMarchal > ( ) ;
public override IBackOfficeInfo GetBackOfficeInfo ( )
= > Mock . Of < IBackOfficeInfo > ( ) ;
public override IHostingEnvironment GetHostingEnvironment ( )
2020-09-30 14:54:53 +02:00
{
2020-10-28 09:41:23 +01:00
var testPath = TestContext . CurrentContext . TestDirectory . Split ( "bin" ) [ 0 ] ;
2020-09-30 14:54:53 +02:00
return new AspNetCoreHostingEnvironment (
2020-12-20 08:36:11 +01:00
Mock . Of < IOptionsMonitor < HostingSettings > > ( x = > x . CurrentValue = = new HostingSettings ( ) ) ,
2021-02-08 11:00:15 +01:00
Mock . Of < IOptionsMonitor < WebRoutingSettings > > ( x = > x . CurrentValue = = new WebRoutingSettings ( ) ) ,
2020-10-28 09:41:23 +01:00
Mock . Of < IWebHostEnvironment > (
2020-12-20 08:36:11 +01:00
x = >
2020-10-28 09:41:23 +01:00
x . WebRootPath = = "/" & &
x . ContentRootPath = = testPath ) ) ;
2020-09-30 14:54:53 +02:00
}
2020-09-28 15:43:28 +02:00
public override IApplicationShutdownRegistry GetHostingEnvironmentLifetime ( )
= > Mock . Of < IApplicationShutdownRegistry > ( ) ;
public override IIpResolver GetIpResolver ( )
= > Mock . Of < IIpResolver > ( ) ;
}
2020-12-20 08:36:11 +01:00
public static ITypeFinder GetTypeFinder ( ) = > s_testHelperInternal . GetTypeFinder ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static TypeLoader GetMockedTypeLoader ( ) = > s_testHelperInternal . GetMockedTypeLoader ( ) ;
2020-09-28 15:43:28 +02:00
2020-10-21 08:50:15 +02:00
public static Lazy < ISqlContext > GetMockSqlContext ( )
{
2020-12-20 08:36:11 +01:00
ISqlContext sqlContext = Mock . Of < ISqlContext > ( ) ;
2020-10-21 08:50:15 +02:00
var syntax = new SqlServerSyntaxProvider ( ) ;
Mock . Get ( sqlContext ) . Setup ( x = > x . SqlSyntax ) . Returns ( syntax ) ;
return new Lazy < ISqlContext > ( ( ) = > sqlContext ) ;
}
2020-10-26 10:47:14 +00:00
public static MapperConfigurationStore CreateMaps ( )
= > new MapperConfigurationStore ( ) ;
2020-10-21 08:50:15 +02:00
2020-12-20 08:36:11 +01:00
//// public static Configs GetConfigs() => _testHelperInternal.GetConfigs();
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IBackOfficeInfo GetBackOfficeInfo ( ) = > s_testHelperInternal . GetBackOfficeInfo ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
//// public static IConfigsFactory GetConfigsFactory() => _testHelperInternal.GetConfigsFactory();
2020-09-28 15:43:28 +02:00
/// <summary>
/// Gets the working directory of the test project.
/// </summary>
/// <value>The assembly directory.</value>
2020-12-20 08:36:11 +01:00
public static string WorkingDirectory = > s_testHelperInternal . WorkingDirectory ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IShortStringHelper ShortStringHelper = > s_testHelperInternal . ShortStringHelper ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IJsonSerializer JsonSerializer = > s_testHelperInternal . JsonSerializer ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IVariationContextAccessor VariationContextAccessor = > s_testHelperInternal . VariationContextAccessor ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IDbProviderFactoryCreator DbProviderFactoryCreator = > s_testHelperInternal . DbProviderFactoryCreator ;
public static IBulkSqlInsertProvider BulkSqlInsertProvider = > s_testHelperInternal . BulkSqlInsertProvider ;
public static IMarchal Marchal = > s_testHelperInternal . Marchal ;
public static CoreDebugSettings CoreDebugSettings = > s_testHelperInternal . CoreDebugSettings ;
public static IIOHelper IOHelper = > s_testHelperInternal . IOHelper ;
public static IMainDom MainDom = > s_testHelperInternal . MainDom ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static UriUtility UriUtility = > s_testHelperInternal . UriUtility ;
public static IEmailSender EmailSender { get ; } = new EmailSender ( Options . Create ( new GlobalSettings ( ) ) ) ;
2020-09-28 15:43:28 +02:00
/// <summary>
/// Some test files are copied to the /bin (/bin/debug) on build, this is a utility to return their physical path based on a virtual path name
/// </summary>
2020-12-20 08:36:11 +01:00
public static string MapPathForTestFiles ( string relativePath ) = > s_testHelperInternal . MapPathForTestFiles ( relativePath ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static void InitializeContentDirectories ( ) = > CreateDirectories ( new [ ] { Constants . SystemDirectories . MvcViews , new GlobalSettings ( ) . UmbracoMediaPath , Constants . SystemDirectories . AppPlugins } ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static void CleanContentDirectories ( ) = > CleanDirectories ( new [ ] { Constants . SystemDirectories . MvcViews , new GlobalSettings ( ) . UmbracoMediaPath } ) ;
2020-09-28 15:43:28 +02:00
public static void CreateDirectories ( string [ ] directories )
{
foreach ( var directory in directories )
{
var directoryInfo = new DirectoryInfo ( IOHelper . MapPath ( directory ) ) ;
if ( directoryInfo . Exists = = false )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
Directory . CreateDirectory ( IOHelper . MapPath ( directory ) ) ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
}
}
public static void CleanDirectories ( string [ ] directories )
{
var preserves = new Dictionary < string , string [ ] >
{
2020-12-20 08:36:11 +01:00
{ Constants . SystemDirectories . MvcViews , new [ ] { "dummy.txt" } }
2020-09-28 15:43:28 +02:00
} ;
2020-12-20 08:36:11 +01:00
2020-09-28 15:43:28 +02:00
foreach ( var directory in directories )
{
var directoryInfo = new DirectoryInfo ( IOHelper . MapPath ( directory ) ) ;
var preserve = preserves . ContainsKey ( directory ) ? preserves [ directory ] : null ;
if ( directoryInfo . Exists )
2020-12-20 08:36:11 +01:00
{
foreach ( FileInfo fileInfo in directoryInfo . GetFiles ( ) . Where ( x = > preserve = = null | | preserve . Contains ( x . Name ) = = false ) )
{
fileInfo . Delete ( ) ;
}
}
2020-09-28 15:43:28 +02:00
}
}
public static void CleanUmbracoSettingsConfig ( )
{
var currDir = new DirectoryInfo ( WorkingDirectory ) ;
var umbracoSettingsFile = Path . Combine ( currDir . Parent . Parent . FullName , "config" , "umbracoSettings.config" ) ;
if ( File . Exists ( umbracoSettingsFile ) )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
File . Delete ( umbracoSettingsFile ) ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
}
// TODO: Move to Assertions or AssertHelper
// FIXME: obsolete the dateTimeFormat thing and replace with dateDelta
public static void AssertPropertyValuesAreEqual ( object actual , object expected , string dateTimeFormat = null , Func < IEnumerable , IEnumerable > sorter = null , string [ ] ignoreProperties = null )
{
const int dateDeltaMilliseconds = 500 ; // .5s
2020-12-20 08:36:11 +01:00
PropertyInfo [ ] properties = expected . GetType ( ) . GetProperties ( ) ;
foreach ( PropertyInfo property in properties )
2020-09-28 15:43:28 +02:00
{
// ignore properties that are attributed with EditorBrowsableState.Never
2020-12-20 08:36:11 +01:00
EditorBrowsableAttribute att = property . GetCustomAttribute < EditorBrowsableAttribute > ( false ) ;
2020-09-28 15:43:28 +02:00
if ( att ! = null & & att . State = = EditorBrowsableState . Never )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
continue ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
// ignore explicitely ignored properties
if ( ignoreProperties ! = null & & ignoreProperties . Contains ( property . Name ) )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
continue ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
var actualValue = property . GetValue ( actual , null ) ;
var expectedValue = property . GetValue ( expected , null ) ;
AssertAreEqual ( property , expectedValue , actualValue , sorter , dateDeltaMilliseconds ) ;
}
}
private static void AssertAreEqual ( PropertyInfo property , object expected , object actual , Func < IEnumerable , IEnumerable > sorter = null , int dateDeltaMilliseconds = 0 )
{
2020-12-20 08:36:11 +01:00
if ( ! ( expected is string ) & & expected is IEnumerable enumerable )
2020-09-28 15:43:28 +02:00
{
// sort property collection by alias, not by property ids
// on members, built-in properties don't have ids (always zero)
if ( expected is PropertyCollection )
2020-12-20 08:36:11 +01:00
{
sorter = e = > ( ( PropertyCollection ) e ) . OrderBy ( x = > x . Alias ) ;
}
2020-09-28 15:43:28 +02:00
// compare lists
2020-12-20 08:36:11 +01:00
AssertListsAreEqual ( property , ( IEnumerable ) actual , enumerable , sorter , dateDeltaMilliseconds ) ;
2020-09-28 15:43:28 +02:00
}
else if ( expected is DateTime expectedDateTime )
{
// compare date & time with delta
2020-12-20 08:36:11 +01:00
var actualDateTime = ( DateTime ) actual ;
2020-09-28 15:43:28 +02:00
var delta = ( actualDateTime - expectedDateTime ) . TotalMilliseconds ;
Assert . IsTrue ( Math . Abs ( delta ) < = dateDeltaMilliseconds , "Property {0}.{1} does not match. Expected: {2} but was: {3}" , property . DeclaringType . Name , property . Name , expected , actual ) ;
}
else if ( expected is Property expectedProperty )
{
// compare values
2020-12-20 08:36:11 +01:00
var actualProperty = ( Property ) actual ;
IPropertyValue [ ] expectedPropertyValues = expectedProperty . Values . OrderBy ( x = > x . Culture ) . ThenBy ( x = > x . Segment ) . ToArray ( ) ;
IPropertyValue [ ] actualPropertyValues = actualProperty . Values . OrderBy ( x = > x . Culture ) . ThenBy ( x = > x . Segment ) . ToArray ( ) ;
2020-09-28 15:43:28 +02:00
if ( expectedPropertyValues . Length ! = actualPropertyValues . Length )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
Assert . Fail ( $"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}." ) ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
for ( var i = 0 ; i < expectedPropertyValues . Length ; i + + )
{
Assert . AreEqual ( expectedPropertyValues [ i ] . EditedValue , actualPropertyValues [ i ] . EditedValue , $"{property.DeclaringType.Name}.{property.Name}: Expected draft value \" { expectedPropertyValues [ i ] . EditedValue } \ " but got \"{actualPropertyValues[i].EditedValue}\"." ) ;
Assert . AreEqual ( expectedPropertyValues [ i ] . PublishedValue , actualPropertyValues [ i ] . PublishedValue , $"{property.DeclaringType.Name}.{property.Name}: Expected published value \" { expectedPropertyValues [ i ] . EditedValue } \ " but got \"{actualPropertyValues[i].EditedValue}\"." ) ;
}
}
else if ( expected is IDataEditor expectedEditor )
{
Assert . IsInstanceOf < IDataEditor > ( actual ) ;
2020-12-20 08:36:11 +01:00
var actualEditor = ( IDataEditor ) actual ;
2020-09-28 15:43:28 +02:00
Assert . AreEqual ( expectedEditor . Alias , actualEditor . Alias ) ;
2020-12-20 08:36:11 +01:00
2020-09-28 15:43:28 +02:00
// what else shall we test?
}
else
{
// directly compare values
2020-12-20 08:36:11 +01:00
Assert . AreEqual (
expected ,
actual ,
"Property {0}.{1} does not match. Expected: {2} but was: {3}" ,
property . DeclaringType . Name ,
property . Name ,
expected ? . ToString ( ) ? ? "<null>" ,
actual ? . ToString ( ) ? ? "<null>" ) ;
2020-09-28 15:43:28 +02:00
}
}
private static void AssertListsAreEqual ( PropertyInfo property , IEnumerable expected , IEnumerable actual , Func < IEnumerable , IEnumerable > sorter = null , int dateDeltaMilliseconds = 0 )
{
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 < IEntity > ( ) . ToList ( ) ;
2020-12-20 08:36:11 +01:00
return entities . Count > 0 ? ( IEnumerable ) entities . OrderBy ( x = > x . Id ) : entities ;
2020-09-28 15:43:28 +02:00
} ;
}
var expectedListEx = sorter ( expected ) . Cast < object > ( ) . ToList ( ) ;
var actualListEx = sorter ( actual ) . Cast < object > ( ) . ToList ( ) ;
if ( actualListEx . Count ! = expectedListEx . Count )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
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 ) ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
for ( var i = 0 ; i < actualListEx . Count ; i + + )
2020-12-20 08:36:11 +01:00
{
2020-09-28 15:43:28 +02:00
AssertAreEqual ( property , expectedListEx [ i ] , actualListEx [ i ] , sorter , dateDeltaMilliseconds ) ;
2020-12-20 08:36:11 +01:00
}
2020-09-28 15:43:28 +02:00
}
2020-12-20 08:36:11 +01:00
public static IUmbracoVersion GetUmbracoVersion ( ) = > s_testHelperInternal . GetUmbracoVersion ( ) ;
2020-09-28 15:43:28 +02:00
2020-10-30 11:16:17 +00:00
public static IServiceCollection GetServiceCollection ( ) = > new ServiceCollection ( ) . AddLazySupport ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IHostingEnvironment GetHostingEnvironment ( ) = > s_testHelperInternal . GetHostingEnvironment ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static ILoggingConfiguration GetLoggingConfiguration ( IHostingEnvironment hostingEnv ) = > s_testHelperInternal . GetLoggingConfiguration ( hostingEnv ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IApplicationShutdownRegistry GetHostingEnvironmentLifetime ( ) = > s_testHelperInternal . GetHostingEnvironmentLifetime ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IIpResolver GetIpResolver ( ) = > s_testHelperInternal . GetIpResolver ( ) ;
2020-09-28 15:43:28 +02:00
2020-12-20 08:36:11 +01:00
public static IRequestCache GetRequestCache ( ) = > s_testHelperInternal . GetRequestCache ( ) ;
2020-09-30 14:54:53 +02:00
2020-12-20 08:36:11 +01:00
public static IPublishedUrlProvider GetPublishedUrlProvider ( ) = > s_testHelperInternal . GetPublishedUrlProvider ( ) ;
2020-09-28 15:43:28 +02:00
}
}