From a87d534adea58c44ca9bc2602795166708cb92da Mon Sep 17 00:00:00 2001 From: Brandon Stirnaman Date: Mon, 21 Oct 2013 11:09:09 -0500 Subject: [PATCH] Updated IoC, new Wait with inline timeout, multi-browser execution, updated to 2.2 --- FluentAutomation.Remote/RemoteFluentTest.cs | 23 +- .../CommandProvider.cs | 18 +- .../CommandProviderList.cs | 16 + .../FluentAutomation.SeleniumWebDriver.csproj | 12 +- .../MultiCommandProvider.cs | 219 ++++++ .../MultiExpectProvider.cs | 109 +++ .../SeleniumWebDriver.cs | 229 ++++--- .../Wrappers/EnhancedRemoteWebDriver.cs | 40 ++ .../Wrappers/IEDriver.cs | 8 +- .../packages.config | 4 +- .../FluentAutomation.Tests.csproj | 2 + FluentAutomation.Tests/Native/SessionTests.cs | 118 ++++ FluentAutomation/3rdPartyLib/TinyIoC.cs | 622 ++++++++++++++++-- FluentAutomation/ActionSyntaxProvider.cs | 20 + FluentAutomation/BaseFluentTest.cs | 23 +- FluentAutomation/EmbeddedResources.cs | 50 +- FluentAutomation/FluentAutomation.csproj | 1 + FluentAutomation/FluentSession.cs | 88 +++ FluentAutomation/FluentTest.cs | 24 +- FluentAutomation/Properties/AssemblyGlobal.cs | 2 +- 20 files changed, 1410 insertions(+), 218 deletions(-) create mode 100644 FluentAutomation.SeleniumWebDriver/CommandProviderList.cs create mode 100644 FluentAutomation.SeleniumWebDriver/MultiCommandProvider.cs create mode 100644 FluentAutomation.SeleniumWebDriver/MultiExpectProvider.cs create mode 100644 FluentAutomation.SeleniumWebDriver/Wrappers/EnhancedRemoteWebDriver.cs create mode 100644 FluentAutomation.Tests/Native/SessionTests.cs create mode 100644 FluentAutomation/FluentSession.cs diff --git a/FluentAutomation.Remote/RemoteFluentTest.cs b/FluentAutomation.Remote/RemoteFluentTest.cs index ad38f11..fd1e247 100644 --- a/FluentAutomation.Remote/RemoteFluentTest.cs +++ b/FluentAutomation.Remote/RemoteFluentTest.cs @@ -12,15 +12,30 @@ public IRemoteActionSyntaxProvider I { get { - var provider = syntaxProvider as IRemoteActionSyntaxProvider; + var provider = SyntaxProvider as IRemoteActionSyntaxProvider; if (provider == null || provider.IsDisposed()) { // register types - FluentAutomation.Settings.Registration(this.Container); - syntaxProvider = this.Container.Resolve(); + this.Session.BootstrapTypeRegistration(FluentAutomation.Settings.Registration); + SyntaxProvider = this.Session.GetSyntaxProvider(); } - return syntaxProvider as IRemoteActionSyntaxProvider; + return SyntaxProvider as IRemoteActionSyntaxProvider; + } + } + + private FluentSession session = null; + public FluentSession Session + { + get + { + if (session == null) + { + session = new FluentSession(); + session.RegisterSyntaxProvider(); + } + + return session; } } } diff --git a/FluentAutomation.SeleniumWebDriver/CommandProvider.cs b/FluentAutomation.SeleniumWebDriver/CommandProvider.cs index 60993ba..57612a1 100644 --- a/FluentAutomation.SeleniumWebDriver/CommandProvider.cs +++ b/FluentAutomation.SeleniumWebDriver/CommandProvider.cs @@ -400,15 +400,15 @@ public void UploadFile(Func element, int x, int y, string fileName) // wait before typing in the field var task = Task.Factory.StartNew(() => { - switch (SeleniumWebDriver.SelectedBrowser) - { - case SeleniumWebDriver.Browser.Firefox: - this.Wait(TimeSpan.FromMilliseconds(1000)); - break; - case SeleniumWebDriver.Browser.Chrome: - this.Wait(TimeSpan.FromMilliseconds(1500)); - break; - } + //switch (SeleniumWebDriver.SelectedBrowser) + //{ + // case SeleniumWebDriver.Browser.Firefox: + // this.Wait(TimeSpan.FromMilliseconds(1000)); + // break; + // case SeleniumWebDriver.Browser.Chrome: + // this.Wait(TimeSpan.FromMilliseconds(1500)); + // break; + //} this.Type(fileName); }); diff --git a/FluentAutomation.SeleniumWebDriver/CommandProviderList.cs b/FluentAutomation.SeleniumWebDriver/CommandProviderList.cs new file mode 100644 index 0000000..72166be --- /dev/null +++ b/FluentAutomation.SeleniumWebDriver/CommandProviderList.cs @@ -0,0 +1,16 @@ +using FluentAutomation.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FluentAutomation.Interfaces +{ + public class CommandProviderList : List + { + public CommandProviderList(IEnumerable collection) + :base(collection) + { + } + } +} diff --git a/FluentAutomation.SeleniumWebDriver/FluentAutomation.SeleniumWebDriver.csproj b/FluentAutomation.SeleniumWebDriver/FluentAutomation.SeleniumWebDriver.csproj index 00785c0..01e3b08 100644 --- a/FluentAutomation.SeleniumWebDriver/FluentAutomation.SeleniumWebDriver.csproj +++ b/FluentAutomation.SeleniumWebDriver/FluentAutomation.SeleniumWebDriver.csproj @@ -47,13 +47,13 @@ - + False - ..\packages\Selenium.WebDriver.2.34.0\lib\net40\WebDriver.dll + ..\packages\Selenium.WebDriver.2.37.0\lib\net40\WebDriver.dll - + False - ..\packages\Selenium.Support.2.34.0\lib\net40\WebDriver.Support.dll + ..\packages\Selenium.Support.2.37.0\lib\net40\WebDriver.Support.dll @@ -62,9 +62,13 @@ + + + + diff --git a/FluentAutomation.SeleniumWebDriver/MultiCommandProvider.cs b/FluentAutomation.SeleniumWebDriver/MultiCommandProvider.cs new file mode 100644 index 0000000..00682a0 --- /dev/null +++ b/FluentAutomation.SeleniumWebDriver/MultiCommandProvider.cs @@ -0,0 +1,219 @@ +using FluentAutomation.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FluentAutomation +{ + public class MultiCommandProvider : ICommandProvider + { + private readonly CommandProviderList commandProviders = null; + + public MultiCommandProvider(CommandProviderList commandProviders) + { + this.commandProviders = commandProviders; + } + + public Uri Url + { + get { return this.commandProviders.First().Url; } + } + + public void Navigate(Uri url) + { + Parallel.ForEach(this.commandProviders, x => x.Navigate(url)); + } + + public Func Find(string selector) + { + throw new NotImplementedException("Find commands don't work with multi-browser testing"); + } + + public Func> FindMultiple(string selector) + { + throw new NotImplementedException("Find commands don't work with multi-browser testing"); + } + + public void Click(int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.Click(x, y)); + } + + public void Click(Func element, int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.Click(xx.Find(element().Selector), x, y)); + } + + public void Click(Func element) + { + Parallel.ForEach(this.commandProviders, x => x.Click(x.Find(element().Selector))); + } + + public void DoubleClick(int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.DoubleClick(x, y)); + } + + public void DoubleClick(Func element, int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.DoubleClick(xx.Find(element().Selector), x, y)); + } + + public void DoubleClick(Func element) + { + Parallel.ForEach(this.commandProviders, x => x.DoubleClick(x.Find(element().Selector))); + } + + public void RightClick(Func element) + { + Parallel.ForEach(this.commandProviders, x => x.RightClick(x.Find(element().Selector))); + } + + public void Hover(int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.Hover(x, y)); + } + + public void Hover(Func element, int x, int y) + { + Parallel.ForEach(this.commandProviders, xx => xx.Hover(xx.Find(element().Selector), x, y)); + } + + public void Hover(Func element) + { + Parallel.ForEach(this.commandProviders, x => x.Hover(x.Find(element().Selector))); + } + + public void Focus(Func element) + { + Parallel.ForEach(this.commandProviders, x => x.Focus(x.Find(element().Selector))); + } + + public void DragAndDrop(int sourceX, int sourceY, int destinationX, int destinationY) + { + Parallel.ForEach(this.commandProviders, x => x.DragAndDrop(sourceX, sourceY, destinationX, destinationY)); + } + + public void DragAndDrop(Func source, Func target) + { + Parallel.ForEach(this.commandProviders, xx => xx.DragAndDrop(xx.Find(source().Selector), xx.Find(target().Selector))); + } + + public void EnterText(Func element, string text) + { + Parallel.ForEach(this.commandProviders, x => x.EnterText(x.Find(element().Selector), text)); + } + + public void EnterTextWithoutEvents(Func element, string text) + { + Parallel.ForEach(this.commandProviders, x => x.EnterTextWithoutEvents(x.Find(element().Selector), text)); + } + + public void AppendText(Func element, string text) + { + Parallel.ForEach(this.commandProviders, x => x.AppendText(x.Find(element().Selector), text)); + } + + public void AppendTextWithoutEvents(Func element, string text) + { + Parallel.ForEach(this.commandProviders, x => x.AppendTextWithoutEvents(x.Find(element().Selector), text)); + } + + public void SelectText(Func element, string optionText) + { + Parallel.ForEach(this.commandProviders, x => x.SelectText(x.Find(element().Selector), optionText)); + } + + public void SelectValue(Func element, string optionValue) + { + Parallel.ForEach(this.commandProviders, x => x.SelectValue(x.Find(element().Selector), optionValue)); + } + + public void SelectIndex(Func element, int optionIndex) + { + Parallel.ForEach(this.commandProviders, x => x.SelectIndex(x.Find(element().Selector), optionIndex)); + } + + public void MultiSelectText(Func element, string[] optionTextCollection) + { + Parallel.ForEach(this.commandProviders, xx => xx.MultiSelectText(xx.Find(element().Selector), optionTextCollection)); + } + + public void MultiSelectValue(Func element, string[] optionValues) + { + Parallel.ForEach(this.commandProviders, x => x.MultiSelectValue(x.Find(element().Selector), optionValues)); + } + + public void MultiSelectIndex(Func element, int[] optionIndices) + { + Parallel.ForEach(this.commandProviders, x => x.MultiSelectIndex(x.Find(element().Selector), optionIndices)); + } + + public void TakeScreenshot(string screenshotName) + { + Parallel.ForEach(this.commandProviders, x => x.TakeScreenshot(screenshotName)); + } + + public void UploadFile(Func element, int x, int y, string fileName) + { + Parallel.ForEach(this.commandProviders, xx => xx.UploadFile(xx.Find(element().Selector), x, y, fileName)); + } + + public void Wait() + { + this.commandProviders.First().Wait(); + } + + public void Wait(int seconds) + { + this.commandProviders.First().Wait(seconds); + } + + public void Wait(TimeSpan timeSpan) + { + this.commandProviders.First().Wait(timeSpan); + } + + public void WaitUntil(System.Linq.Expressions.Expression> conditionFunc) + { + Parallel.ForEach(this.commandProviders, x => x.WaitUntil(conditionFunc)); + } + + public void WaitUntil(System.Linq.Expressions.Expression> conditionFunc, TimeSpan timeout) + { + Parallel.ForEach(this.commandProviders, x => x.WaitUntil(conditionFunc, timeout)); + } + + public void WaitUntil(System.Linq.Expressions.Expression conditionAction) + { + Parallel.ForEach(this.commandProviders, x => x.WaitUntil(conditionAction)); + } + + public void WaitUntil(System.Linq.Expressions.Expression conditionAction, TimeSpan timeout) + { + Parallel.ForEach(this.commandProviders, x => x.WaitUntil(conditionAction, timeout)); + } + + public void Press(string keys) + { + throw new NotImplementedException("Win32 based events are not currently functioning in multi-browser tests"); + } + + public void Type(string text) + { + throw new NotImplementedException("Win32 based events are not currently functioning in multi-browser tests"); + } + + public void Act(Action action, bool waitableAction = true) + { + Parallel.ForEach(this.commandProviders, x => x.Act(action, waitableAction)); + } + + public void Dispose() + { + Parallel.ForEach(this.commandProviders, x => x.Dispose()); + } + } +} diff --git a/FluentAutomation.SeleniumWebDriver/MultiExpectProvider.cs b/FluentAutomation.SeleniumWebDriver/MultiExpectProvider.cs new file mode 100644 index 0000000..bfdaf32 --- /dev/null +++ b/FluentAutomation.SeleniumWebDriver/MultiExpectProvider.cs @@ -0,0 +1,109 @@ +using FluentAutomation.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FluentAutomation +{ + public class MultiExpectProvider : IExpectProvider + { + private readonly List> providers = null; + + public MultiExpectProvider(CommandProviderList commandProviders) + { + this.providers = commandProviders.Select(x => new KeyValuePair(new ExpectProvider(x), x)).ToList(); + } + + public void Count(string selector, int count) + { + Parallel.ForEach(this.providers, x => x.Key.Count(selector, count)); + } + + public void Count(Func> elements, int count) + { + Parallel.ForEach(this.providers, x => x.Key.Count(x.Value.FindMultiple(elements().First().Selector), count)); + } + + public void CssClass(string selector, string className) + { + Parallel.ForEach(this.providers, x => x.Key.CssClass(selector, className)); + } + + public void CssClass(Func element, string className) + { + Parallel.ForEach(this.providers, x => x.Key.CssClass(x.Value.Find(element().Selector), className)); + } + + public void Text(string selector, string text) + { + Parallel.ForEach(this.providers, x => x.Key.Text(selector, text)); + } + + public void Text(Func element, string text) + { + Parallel.ForEach(this.providers, x => x.Key.Text(x.Value.Find(element().Selector), text)); + } + + public void Text(string selector, System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.Text(selector, matchFunc)); + } + + public void Text(Func element, System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.Text(x.Value.Find(element().Selector), matchFunc)); + } + + public void Value(string selector, string value) + { + Parallel.ForEach(this.providers, x => x.Key.Value(selector, value)); + } + + public void Value(Func element, string value) + { + Parallel.ForEach(this.providers, x => x.Key.Value(x.Value.Find(element().Selector), value)); + } + + public void Value(string selector, System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.Value(selector, matchFunc)); + } + + public void Value(Func element, System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.Value(x.Value.Find(element().Selector), matchFunc)); + } + + public void Url(Uri expectedUrl) + { + Parallel.ForEach(this.providers, x => x.Key.Url(expectedUrl)); + } + + public void Url(System.Linq.Expressions.Expression> urlExpression) + { + Parallel.ForEach(this.providers, x => x.Key.Url(urlExpression)); + } + + public void True(System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.True(matchFunc)); + } + + public void False(System.Linq.Expressions.Expression> matchFunc) + { + Parallel.ForEach(this.providers, x => x.Key.False(matchFunc)); + } + + public void Throws(System.Linq.Expressions.Expression matchAction) + { + Parallel.ForEach(this.providers, x => x.Key.Throws(matchAction)); + } + + public void Exists(string selector) + { + Parallel.ForEach(this.providers, x => x.Key.Exists(selector)); + } + } +} diff --git a/FluentAutomation.SeleniumWebDriver/SeleniumWebDriver.cs b/FluentAutomation.SeleniumWebDriver/SeleniumWebDriver.cs index fb3cb5f..fdd3f34 100644 --- a/FluentAutomation.SeleniumWebDriver/SeleniumWebDriver.cs +++ b/FluentAutomation.SeleniumWebDriver/SeleniumWebDriver.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using FluentAutomation.Exceptions; using FluentAutomation.Interfaces; +using FluentAutomation.Wrappers; using OpenQA.Selenium; using OpenQA.Selenium.Remote; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; namespace FluentAutomation { @@ -41,15 +42,30 @@ public enum Browser Chrome = 4, /// - /// PhantomJS - Headless browser - Support is Experimental + /// PhantomJS - Experimental - Headless browser /// - PhantomJs = 5 - } + PhantomJs = 5, - /// - /// Currently selected . - /// - public static Browser SelectedBrowser; + /// + /// Safari - Experimental - Only usable with a Remote URI + /// + Safari = 6, + + /// + /// iPad - Experimental - Only usable with a Remote URI + /// + iPad = 7, + + /// + /// iPhone - Experimental - Only usable with a Remote URI + /// + iPhone = 8, + + /// + /// Android - Experimental - Only usable with a Remote URI + /// + Android = 9 + } /// /// Bootstrap Selenium provider and utilize Firefox. @@ -65,74 +81,54 @@ public static void Bootstrap() /// public static void Bootstrap(Browser browser) { - SeleniumWebDriver.SelectedBrowser = browser; - FluentAutomation.Settings.Registration = (container) => { container.Register(); container.Register(); container.Register(); - - switch (SeleniumWebDriver.SelectedBrowser) - { - case Browser.InternetExplorer: - EmbeddedResources.UnpackFromAssembly("IEDriverServer32.exe", "IEDriverServer.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); - container.Register().AsMultiInstance(); - break; - case Browser.InternetExplorer64: - EmbeddedResources.UnpackFromAssembly("IEDriverServer64.exe", "IEDriverServer.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); - container.Register().AsMultiInstance(); - break; - case Browser.Firefox: - container.Register().AsMultiInstance(); - break; - case Browser.Chrome: - EmbeddedResources.UnpackFromAssembly("chromedriver.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); - container.Register().AsMultiInstance(); - break; - case Browser.PhantomJs: - EmbeddedResources.UnpackFromAssembly("phantomjs.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); - container.Register().AsMultiInstance(); - break; - } + + var browserDriver = GenerateBrowserSpecificDriver(browser); + container.Register((c, o) => browserDriver()); + }; + } + + public static void Bootstrap(params Browser[] browsers) + { + if (browsers.Length == 1) + { + Bootstrap(browsers.First()); + return; + } + + FluentAutomation.Settings.Registration = (container) => + { + var webDrivers = new List>(); + browsers.Distinct().ToList().ForEach(x => webDrivers.Add(GenerateBrowserSpecificDriver(x))); + + var commandProviders = new CommandProviderList(webDrivers.Select(x => new CommandProvider(x, new LocalFileStoreProvider()))); + container.Register(commandProviders); + + container.Register(); + container.Register(); + container.Register(); }; } /// - /// Bootstrap Selenium provider using a Remote web driver targetting the requested browser + /// Bootstrap Selenium provider using a Remote web driver targeting the requested browser /// /// - /// + /// public static void Bootstrap(Uri driverUri, Browser browser) { - SeleniumWebDriver.SelectedBrowser = browser; - FluentAutomation.Settings.Registration = (container) => { container.Register(); container.Register(); container.Register(); - DesiredCapabilities browserCapabilities = null; - - switch (SeleniumWebDriver.SelectedBrowser) - { - case Browser.InternetExplorer: - case Browser.InternetExplorer64: - browserCapabilities = DesiredCapabilities.InternetExplorer(); - break; - case Browser.Firefox: - browserCapabilities = DesiredCapabilities.Firefox(); - break; - case Browser.Chrome: - browserCapabilities = DesiredCapabilities.Chrome(); - break; - case Browser.PhantomJs: - browserCapabilities = DesiredCapabilities.PhantomJS(); - break; - } - - container.Register(new RemoteWebDriver(driverUri, browserCapabilities)); + DesiredCapabilities browserCapabilities = GenerateDesiredCapabilities(browser); + container.Register(new EnhancedRemoteWebDriver(driverUri, browserCapabilities)); }; } @@ -141,7 +137,7 @@ public static void Bootstrap(Uri driverUri, Browser browser) /// /// /// - public static void Bootstrap(Uri driverUri, Dictionary capabilities) + public static void Bootstrap(Uri driverUri, Browser browser, Dictionary capabilities) { FluentAutomation.Settings.Registration = (container) => { @@ -149,39 +145,90 @@ public static void Bootstrap(Uri driverUri, Dictionary capabilit container.Register(); container.Register(); - DesiredCapabilities browserCapabilities = null; - - switch (SeleniumWebDriver.SelectedBrowser) + DesiredCapabilities browserCapabilities = GenerateDesiredCapabilities(browser); + foreach (var cap in capabilities) { - case Browser.InternetExplorer: - case Browser.InternetExplorer64: - browserCapabilities = DesiredCapabilities.InternetExplorer(); - break; - case Browser.Firefox: - browserCapabilities = DesiredCapabilities.Firefox(); - break; - case Browser.Chrome: - browserCapabilities = DesiredCapabilities.Chrome(); - break; - case Browser.PhantomJs: - browserCapabilities = DesiredCapabilities.PhantomJS(); - break; + browserCapabilities.SetCapability(cap.Key, cap.Value); } - if (browserCapabilities == null) - { - browserCapabilities = new DesiredCapabilities(capabilities); - } - else - { - foreach (var cap in capabilities) - { - browserCapabilities.SetCapability(cap.Key, cap.Value); - } - } + container.Register(new EnhancedRemoteWebDriver(driverUri, browserCapabilities)); + }; + } - container.Register(new RemoteWebDriver(driverUri, browserCapabilities)); + public static void Bootstrap(Uri driverUri, Dictionary capabilities) + { + FluentAutomation.Settings.Registration = (container) => + { + container.Register(); + container.Register(); + container.Register(); + + DesiredCapabilities browserCapabilities = new DesiredCapabilities(capabilities); + container.Register(new EnhancedRemoteWebDriver(driverUri, browserCapabilities)); }; } + + private static Func GenerateBrowserSpecificDriver(Browser browser) + { + string driverPath = string.Empty; + switch (browser) + { + case Browser.InternetExplorer: + driverPath = EmbeddedResources.UnpackFromAssembly("IEDriverServer32.exe", "IEDriverServer.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); + return new Func(() => new Wrappers.IEDriverWrapper(Path.GetDirectoryName(driverPath))); + case Browser.InternetExplorer64: + driverPath = EmbeddedResources.UnpackFromAssembly("IEDriverServer64.exe", "IEDriverServer.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); + return new Func(() => new Wrappers.IEDriverWrapper(Path.GetDirectoryName(driverPath))); + case Browser.Firefox: + return new Func(() => new OpenQA.Selenium.Firefox.FirefoxDriver()); + case Browser.Chrome: + driverPath = EmbeddedResources.UnpackFromAssembly("chromedriver.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); + return new Func(() => new OpenQA.Selenium.Chrome.ChromeDriver(Path.GetDirectoryName(driverPath))); + case Browser.PhantomJs: + driverPath = EmbeddedResources.UnpackFromAssembly("phantomjs.exe", Assembly.GetAssembly(typeof(SeleniumWebDriver))); + return new Func(() => new OpenQA.Selenium.PhantomJS.PhantomJSDriver(Path.GetDirectoryName(driverPath))); + } + + throw new NotImplementedException("Selected browser " + browser.ToString() + " is not supported yet."); + } + + private static DesiredCapabilities GenerateDesiredCapabilities(Browser browser) + { + DesiredCapabilities browserCapabilities = null; + + switch (browser) + { + case Browser.InternetExplorer: + case Browser.InternetExplorer64: + browserCapabilities = DesiredCapabilities.InternetExplorer(); + break; + case Browser.Firefox: + browserCapabilities = DesiredCapabilities.Firefox(); + break; + case Browser.Chrome: + browserCapabilities = DesiredCapabilities.Chrome(); + break; + case Browser.PhantomJs: + browserCapabilities = DesiredCapabilities.PhantomJS(); + break; + case Browser.Safari: + browserCapabilities = DesiredCapabilities.Safari(); + break; + case Browser.iPad: + browserCapabilities = DesiredCapabilities.IPad(); + break; + case Browser.iPhone: + browserCapabilities = DesiredCapabilities.IPhone(); + break; + case Browser.Android: + browserCapabilities = DesiredCapabilities.Android(); + break; + default: + throw new FluentException("Selected browser [{0}] not supported. Unable to determine appropriate capabilities.", browser.ToString()); + } + + browserCapabilities.IsJavaScriptEnabled = true; + return browserCapabilities; + } } } diff --git a/FluentAutomation.SeleniumWebDriver/Wrappers/EnhancedRemoteWebDriver.cs b/FluentAutomation.SeleniumWebDriver/Wrappers/EnhancedRemoteWebDriver.cs new file mode 100644 index 0000000..b31d691 --- /dev/null +++ b/FluentAutomation.SeleniumWebDriver/Wrappers/EnhancedRemoteWebDriver.cs @@ -0,0 +1,40 @@ +using OpenQA.Selenium; +using OpenQA.Selenium.Remote; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FluentAutomation.Wrappers +{ + public class EnhancedRemoteWebDriver : RemoteWebDriver, ITakesScreenshot + { + public EnhancedRemoteWebDriver(ICapabilities desiredCapabilities) + : base(desiredCapabilities) + { + } + + public EnhancedRemoteWebDriver(ICommandExecutor commandExecutor, ICapabilities desiredCapabilities) + : base(commandExecutor, desiredCapabilities) + { + } + + public EnhancedRemoteWebDriver(Uri remoteAddress, ICapabilities desiredCapabilities, TimeSpan commandTimeout) + : base(remoteAddress, desiredCapabilities, commandTimeout) + { + } + + public EnhancedRemoteWebDriver(Uri remoteAddress, ICapabilities desiredCapabilities) + : base(remoteAddress, desiredCapabilities) + { + } + + public Screenshot GetScreenshot() + { + Response response = this.Execute(DriverCommand.Screenshot, null); + string responseContent = response.Value.ToString(); + + return new Screenshot(responseContent); + } + } +} \ No newline at end of file diff --git a/FluentAutomation.SeleniumWebDriver/Wrappers/IEDriver.cs b/FluentAutomation.SeleniumWebDriver/Wrappers/IEDriver.cs index da68ef1..fb19d29 100644 --- a/FluentAutomation.SeleniumWebDriver/Wrappers/IEDriver.cs +++ b/FluentAutomation.SeleniumWebDriver/Wrappers/IEDriver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; @@ -8,7 +9,12 @@ namespace FluentAutomation.Wrappers public class IEDriverWrapper : OpenQA.Selenium.IE.InternetExplorerDriver { public IEDriverWrapper() - : base(new OpenQA.Selenium.IE.InternetExplorerOptions() + : this(Path.GetTempPath()) + { + } + + public IEDriverWrapper(string ieDriverDirectoryPath) + : base(ieDriverDirectoryPath, new OpenQA.Selenium.IE.InternetExplorerOptions() { IgnoreZoomLevel = true, IntroduceInstabilityByIgnoringProtectedModeSettings = true, diff --git a/FluentAutomation.SeleniumWebDriver/packages.config b/FluentAutomation.SeleniumWebDriver/packages.config index 6e90e8d..cbb469b 100644 --- a/FluentAutomation.SeleniumWebDriver/packages.config +++ b/FluentAutomation.SeleniumWebDriver/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/FluentAutomation.Tests/FluentAutomation.Tests.csproj b/FluentAutomation.Tests/FluentAutomation.Tests.csproj index fe1f392..1b32e52 100644 --- a/FluentAutomation.Tests/FluentAutomation.Tests.csproj +++ b/FluentAutomation.Tests/FluentAutomation.Tests.csproj @@ -34,6 +34,7 @@ 4 + ..\packages\xunit.1.9.0.1566\lib\xunit.dll @@ -53,6 +54,7 @@ + diff --git a/FluentAutomation.Tests/Native/SessionTests.cs b/FluentAutomation.Tests/Native/SessionTests.cs new file mode 100644 index 0000000..6cbc602 --- /dev/null +++ b/FluentAutomation.Tests/Native/SessionTests.cs @@ -0,0 +1,118 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace FluentAutomation.Tests.Native +{ + [TestClass] + public class SessionTests : FluentTest + { + [TestMethod] + public void Test1() + { + I.Open("http://localhost:1474?test1"); + } + + [TestMethod] + public void Test2() + { + I.Open("http://localhost:1474?test2"); + } + + [TestMethod] + public void Test3() + { + I.Open("http://localhost:1474?test3"); + } + + [TestMethod] + public void Test4() + { + I.Open("http://localhost:1474?test4"); + } + + [TestMethod] + public void Test5() + { + I.Open("http://localhost:1474?test5"); + } + + [TestMethod] + public void Test6() + { + I.Open("http://localhost:1474?test6"); + } + + //[TestInitialize] + //public void Setup() + //{ + // FluentAutomation.SeleniumWebDriver.Bootstrap(SeleniumWebDriver.Browser.Chrome, SeleniumWebDriver.Browser.InternetExplorer, SeleniumWebDriver.Browser.PhantomJs); + //} + + [ClassInitialize] + public static void Setup(TestContext context) + { + FluentAutomation.SeleniumWebDriver.Bootstrap(SeleniumWebDriver.Browser.Chrome, SeleniumWebDriver.Browser.InternetExplorer, SeleniumWebDriver.Browser.Firefox); + FluentSession.EnableStickySession(); + } + + [ClassCleanup] + public static void Cleanup() + { + FluentSession.DisableStickySession(); + } + } + + //public class XunitSessionTests : FluentTest + //{ + // [Fact] + // public void Test1() + // { + // I.Open("http://localhost:1474?test1"); + // } + + // [Fact] + // public void Test2() + // { + // I.Open("http://localhost:1474?test2"); + // } + + // [Fact] + // public void Test3() + // { + // I.Open("http://localhost:1474?test3"); + // } + + // [Fact] + // public void Test4() + // { + // I.Open("http://localhost:1474?test4"); + // } + + // [Fact] + // public void Test5() + // { + // I.Open("http://localhost:1474?test5"); + // } + + // [Fact] + // public void Test6() + // { + // I.Open("http://localhost:1474?test6"); + // } + + // public static void Setup(TestContext context) + // { + // FluentAutomation.SeleniumWebDriver.Bootstrap(SeleniumWebDriver.Browser.Chrome); + // FluentSession.EnableStickySession(); + // } + + // public static void Cleanup() + // { + // FluentSession.DisableStickySession(); + // } + //} +} diff --git a/FluentAutomation/3rdPartyLib/TinyIoC.cs b/FluentAutomation/3rdPartyLib/TinyIoC.cs index 764e50a..e6a0b04 100644 --- a/FluentAutomation/3rdPartyLib/TinyIoC.cs +++ b/FluentAutomation/3rdPartyLib/TinyIoC.cs @@ -22,16 +22,23 @@ // depending on platform features. If the platform has an appropriate // #DEFINE then these should be set automatically below. #define EXPRESSIONS // Platform supports System.Linq.Expressions +#define COMPILED_EXPRESSIONS // Platform supports compiling expressions #define APPDOMAIN_GETASSEMBLIES // Platform supports getting all assemblies from the AppDomain object #define UNBOUND_GENERICS_GETCONSTRUCTORS // Platform supports GetConstructors on unbound generic types #define GETPARAMETERS_OPEN_GENERICS // Platform supports GetParameters on open generics #define RESOLVE_OPEN_GENERICS // Platform supports resolving open generics +#define READER_WRITER_LOCK_SLIM // Platform supports ReaderWriterLockSlim + +//// NETFX_CORE +//#if NETFX_CORE +//#endif // CompactFramework / Windows Phone 7 // By default does not support System.Linq.Expressions. // AppDomain object does not support enumerating all assemblies in the app domain. #if PocketPC || WINDOWS_PHONE #undef EXPRESSIONS +#undef COMPILED_EXPRESSIONS #undef APPDOMAIN_GETASSEMBLIES #undef UNBOUND_GENERICS_GETCONSTRUCTORS #endif @@ -41,24 +48,160 @@ #if PocketPC #undef GETPARAMETERS_OPEN_GENERICS #undef RESOLVE_OPEN_GENERICS +#undef READER_WRITER_LOCK_SLIM #endif #if SILVERLIGHT #undef APPDOMAIN_GETASSEMBLIES #endif +#if NETFX_CORE +#undef APPDOMAIN_GETASSEMBLIES +#undef RESOLVE_OPEN_GENERICS +#endif + +#if COMPILED_EXPRESSIONS +#define USE_OBJECT_CONSTRUCTOR +#endif + #endregion namespace TinyIoC { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using System.Reflection; + #if EXPRESSIONS using System.Linq.Expressions; + using System.Threading; + +#endif + +#if NETFX_CORE + using System.Threading.Tasks; + using Windows.Storage.Search; + using Windows.Storage; + using Windows.UI.Xaml.Shapes; #endif #region SafeDictionary +#if READER_WRITER_LOCK_SLIM + public class SafeDictionary : IDisposable + { + private readonly ReaderWriterLockSlim _padlock = new ReaderWriterLockSlim(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + _padlock.EnterWriteLock(); + + try + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + finally + { + _padlock.ExitWriteLock(); + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + _padlock.EnterReadLock(); + try + { + return _Dictionary.TryGetValue(key, out value); + } + finally + { + _padlock.ExitReadLock(); + } + } + + public bool Remove(TKey key) + { + _padlock.EnterWriteLock(); + try + { + return _Dictionary.Remove(key); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public void Clear() + { + _padlock.EnterWriteLock(); + try + { + _Dictionary.Clear(); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public IEnumerable Keys + { + get + { + _padlock.EnterReadLock(); + try + { + return new List(_Dictionary.Keys); + } + finally + { + _padlock.ExitReadLock(); + } + } + } + + #region IDisposable Members + + public void Dispose() + { + _padlock.EnterWriteLock(); + + try + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + finally + { + _padlock.ExitWriteLock(); + } + + GC.SuppressFinalize(this); + } + + #endregion + } +#else public class SafeDictionary : IDisposable { private readonly object _Padlock = new object(); @@ -115,7 +258,7 @@ public IEnumerable Keys return _Dictionary.Keys; } } - #region IDisposable Members + #region IDisposable Members public void Dispose() { @@ -136,6 +279,7 @@ where item is IDisposable #endregion } +#endif #endregion #region Extensions @@ -157,11 +301,12 @@ public static Type[] SafeGetTypes(this Assembly assembly) { assemblies = new Type[] { }; } - catch (ReflectionTypeLoadException) +#if !NETFX_CORE + catch (ReflectionTypeLoadException e) { - assemblies = new Type[] { }; + assemblies = e.Types.Where(t => t != null).ToArray(); } - +#endif return assemblies; } } @@ -175,6 +320,34 @@ static TypeExtensions() _genericMethodCache = new SafeDictionary(); } + //#if NETFX_CORE + // /// + // /// Gets a generic method from a type given the method name, generic types and parameter types + // /// + // /// Source type + // /// Name of the method + // /// Generic types to use to make the method generic + // /// Method parameters + // /// MethodInfo or null if no matches found + // /// + // /// + // public static MethodInfo GetGenericMethod(this Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes) + // { + // MethodInfo method; + // var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); + + // // Shouldn't need any additional locking + // // we don't care if we do the method info generation + // // more than once before it gets cached. + // if (!_genericMethodCache.TryGetValue(cacheKey, out method)) + // { + // method = GetMethod(sourceType, methodName, genericTypes, parameterTypes); + // _genericMethodCache[cacheKey] = method; + // } + + // return method; + // } + //#else /// /// Gets a generic method from a type given the method name, binding flags, generic types and parameter types /// @@ -186,7 +359,7 @@ static TypeExtensions() /// MethodInfo or null if no matches found /// /// - public static MethodInfo GetGenericMethod(this Type sourceType, System.Reflection.BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) + public static MethodInfo GetGenericMethod(this Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) { MethodInfo method; var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); @@ -202,13 +375,33 @@ public static MethodInfo GetGenericMethod(this Type sourceType, System.Reflectio return method; } + //#endif +#if NETFX_CORE + private static MethodInfo GetMethod(Type sourceType, BindingFlags flags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + var methods = + sourceType.GetMethods(flags).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( + mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). + Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( + mi => mi.MakeGenericMethod(genericTypes)).Where( + mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); + + if (methods.Count > 1) + { + throw new AmbiguousMatchException(); + } + + return methods.FirstOrDefault(); + } +#else private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) { #if GETPARAMETERS_OPEN_GENERICS var methods = sourceType.GetMethods(bindingFlags).Where( - mi => string.Equals(methodName, mi.Name, StringComparison.InvariantCulture)).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( mi => mi.MakeGenericMethod(genericTypes)).Where( @@ -232,6 +425,7 @@ where genericMethod.GetParameters().Select(pi => pi.ParameterType).SequenceEqual return methods.FirstOrDefault(); } +#endif private sealed class GenericMethodCacheKey { @@ -263,7 +457,7 @@ public override bool Equals(object obj) if (_sourceType != cacheKey._sourceType) return false; - if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.InvariantCulture)) + if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.Ordinal)) return false; if (_genericTypes.Length != cacheKey._genericTypes.Length) @@ -314,7 +508,21 @@ private int GenerateHashCode() } } } + } + + // @mbrit - 2012-05-22 - shim for ForEach call on List... +#if NETFX_CORE + internal static class ListExtender + { + internal static void ForEach(this List list, Action callback) + { + foreach (T obj in list) + callback(obj); + } + } +#endif + #endregion #region TinyIoC Exception Types @@ -498,6 +706,13 @@ public enum NamedResolutionFailureActions Fail } + public enum DuplicateImplementationActions + { + RegisterSingle, + RegisterMultiple, + Fail + } + /// /// Resolution settings /// @@ -570,6 +785,53 @@ public static ResolveOptions FailUnregisteredOnly public sealed partial class TinyIoCContainer : IDisposable { + #region Fake NETFX_CORE Classes +#if NETFX_CORE + private sealed class MethodAccessException : Exception + { + } + + private sealed class AppDomain + { + public static AppDomain CurrentDomain { get; private set; } + + static AppDomain() + { + CurrentDomain = new AppDomain(); + } + + // @mbrit - 2012-05-30 - in WinRT, this should be done async... + public async Task> GetAssembliesAsync() + { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + + List assemblies = new List(); + + var files = await folder.GetFilesAsync(); + + foreach (StorageFile file in files) + { + if (file.FileType == ".dll" || file.FileType == ".exe") + { + AssemblyName name = new AssemblyName() { Name = System.IO.Path.GetFileNameWithoutExtension(file.Name) }; + try + { + var asm = Assembly.Load(name); + assemblies.Add(asm); + } + catch + { + // ignore exceptions here... + } + } + } + + return assemblies; + } + } +#endif + #endregion + #region "Fluent" API /// /// Registration options for "fluent" API @@ -768,9 +1030,9 @@ public TinyIoCContainer GetChildContainer() public void AutoRegister() { #if APPDOMAIN_GETASSEMBLIES - AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), true, null); + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, null); #else - AutoRegisterInternal(new Assembly[] {this.GetType().Assembly}, true, null); + AutoRegisterInternal(new Assembly[] {this.GetType().Assembly()}, true, null); #endif } @@ -785,23 +1047,23 @@ public void AutoRegister() public void AutoRegister(Func registrationPredicate) { #if APPDOMAIN_GETASSEMBLIES - AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), true, registrationPredicate); + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, registrationPredicate); #else - AutoRegisterInternal(new Assembly[] {this.GetType().Assembly}, true, registrationPredicate); + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly()}, true, registrationPredicate); #endif } /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// - /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// What action to take when encountering duplicate implementations of an interface/base class. /// - public void AutoRegister(bool ignoreDuplicateImplementations) + public void AutoRegister(DuplicateImplementationActions duplicateAction) { #if APPDOMAIN_GETASSEMBLIES - AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), ignoreDuplicateImplementations, null); + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, null); #else - AutoRegisterInternal(new Assembly[] { this.GetType().Assembly }, ignoreDuplicateImplementations, null); + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, ignoreDuplicateImplementations, null); #endif } @@ -809,15 +1071,15 @@ public void AutoRegister(bool ignoreDuplicateImplementations) /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// Types will only be registered if they pass the supplied registration predicate. /// - /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// What action to take when encountering duplicate implementations of an interface/base class. /// Predicate to determine if a particular type should be registered /// - public void AutoRegister(bool ignoreDuplicateImplementations, Func registrationPredicate) + public void AutoRegister(DuplicateImplementationActions duplicateAction, Func registrationPredicate) { #if APPDOMAIN_GETASSEMBLIES - AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), ignoreDuplicateImplementations, registrationPredicate); + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, registrationPredicate); #else - AutoRegisterInternal(new Assembly[] { this.GetType().Assembly }, ignoreDuplicateImplementations, registrationPredicate); + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, ignoreDuplicateImplementations, registrationPredicate); #endif } @@ -830,7 +1092,7 @@ public void AutoRegister(bool ignoreDuplicateImplementations, Func r /// Assemblies to process public void AutoRegister(IEnumerable assemblies) { - AutoRegisterInternal(assemblies, true, null); + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, null); } /// @@ -844,18 +1106,18 @@ public void AutoRegister(IEnumerable assemblies) /// Predicate to determine if a particular type should be registered public void AutoRegister(IEnumerable assemblies, Func registrationPredicate) { - AutoRegisterInternal(assemblies, true, registrationPredicate); + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, registrationPredicate); } /// /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies /// /// Assemblies to process - /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// What action to take when encountering duplicate implementations of an interface/base class. /// - public void AutoRegister(IEnumerable assemblies, bool ignoreDuplicateImplementations) + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction) { - AutoRegisterInternal(assemblies, ignoreDuplicateImplementations, null); + AutoRegisterInternal(assemblies, duplicateAction, null); } /// @@ -863,12 +1125,12 @@ public void AutoRegister(IEnumerable assemblies, bool ignoreDuplicateI /// Types will only be registered if they pass the supplied registration predicate. /// /// Assemblies to process - /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// What action to take when encountering duplicate implementations of an interface/base class. /// Predicate to determine if a particular type should be registered /// - public void AutoRegister(IEnumerable assemblies, bool ignoreDuplicateImplementations, Func registrationPredicate) + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) { - AutoRegisterInternal(assemblies, ignoreDuplicateImplementations, registrationPredicate); + AutoRegisterInternal(assemblies, duplicateAction, registrationPredicate); } /// @@ -1153,11 +1415,25 @@ public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable< throw new ArgumentNullException("types", "types is null."); foreach (var type in implementationTypes) + //#if NETFX_CORE + // if (!registrationType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + //#else if (!registrationType.IsAssignableFrom(type)) + //#endif throw new ArgumentException(String.Format("types: The type {0} is not assignable from {1}", registrationType.FullName, type.FullName)); if (implementationTypes.Count() != implementationTypes.Distinct().Count()) - throw new ArgumentException("types: The same implementation type cannot be specificed multiple times"); + { + var queryForDuplicatedTypes = from i in implementationTypes + group i by i + into j + where j.Count() > 1 + select j.Key.FullName; + + var fullNamesOfDuplicatedTypes = string.Join(",\n", queryForDuplicatedTypes.ToArray()); + var multipleRegMessage = string.Format("types: The same implementation type cannot be specified multiple times for {0}\n\n{1}", registrationType.FullName, fullNamesOfDuplicatedTypes); + throw new ArgumentException(multipleRegMessage); + } var registerOptions = new List(); @@ -2198,9 +2474,13 @@ private class MultiInstanceFactory : ObjectFactoryBase public MultiInstanceFactory(Type registerType, Type registerImplementation) { - if (registerImplementation.IsAbstract || registerImplementation.IsInterface) + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + // throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); - + //#endif if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); @@ -2524,7 +2804,11 @@ private class SingletonFactory : ObjectFactoryBase, IDisposable public SingletonFactory(Type registerType, Type registerImplementation) { - if (registerImplementation.IsAbstract || registerImplementation.IsInterface) + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); if (!IsValidAssignment(registerType, registerImplementation)) @@ -2611,7 +2895,11 @@ public CustomObjectLifetimeFactory(Type registerType, Type registerImplementatio if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); - if (registerImplementation.IsAbstract || registerImplementation.IsInterface) + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif throw new TinyIoCRegistrationTypeException(registerImplementation, errorMessage); this.registerType = registerType; @@ -2743,6 +3031,10 @@ public override int GetHashCode() } } private readonly SafeDictionary _RegisteredTypes; +#if USE_OBJECT_CONSTRUCTOR + private delegate object ObjectConstructor(params object[] parameters); + private static readonly SafeDictionary _ObjectConstructorCache = new SafeDictionary(); +#endif #endregion #region Constructors @@ -2763,14 +3055,14 @@ private TinyIoCContainer(TinyIoCContainer parent) #region Internal Methods private readonly object _AutoRegisterLock = new object(); - private void AutoRegisterInternal(IEnumerable assemblies, bool ignoreDuplicateImplementations, Func registrationPredicate) + private void AutoRegisterInternal(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) { lock (_AutoRegisterLock) { var types = assemblies.SelectMany(a => a.SafeGetTypes()).Where(t => !IsIgnoredType(t, registrationPredicate)).ToList(); var concreteTypes = from type in types - where (type.IsClass == true) && (type.IsAbstract == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition)) + where type.IsClass() && (type.IsAbstract() == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition())) select type; foreach (var type in concreteTypes) @@ -2786,17 +3078,26 @@ private void AutoRegisterInternal(IEnumerable assemblies, bool ignoreD } var abstractInterfaceTypes = from type in types - where ((type.IsInterface == true || type.IsAbstract == true) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition)) + where ((type.IsInterface() || type.IsAbstract()) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition())) select type; foreach (var type in abstractInterfaceTypes) { + var localType = type; var implementations = from implementationType in concreteTypes - where implementationType.GetInterfaces().Contains(type) || implementationType.BaseType == type + where localType.IsAssignableFrom(implementationType) select implementationType; - if (!ignoreDuplicateImplementations && implementations.Count() > 1) - throw new TinyIoCAutoRegistrationException(type, implementations); + if (implementations.Count() > 1) + { + if (duplicateAction == DuplicateImplementationActions.Fail) + throw new TinyIoCAutoRegistrationException(type, implementations); + + if (duplicateAction == DuplicateImplementationActions.RegisterMultiple) + { + RegisterMultiple(type, implementations); + } + } var firstImplementation = implementations.FirstOrDefault(); if (firstImplementation != null) @@ -2819,15 +3120,13 @@ private bool IsIgnoredAssembly(Assembly assembly) // TODO - find a better way to remove "system" assemblies from the auto registration var ignoreChecks = new List>() { - asm => asm.FullName.StartsWith("Microsoft.", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("System.", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("System,", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("mscorlib,", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("IronPython", StringComparison.InvariantCulture), - asm => asm.FullName.StartsWith("IronRuby", StringComparison.InvariantCulture), + asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal), }; foreach (var check in ignoreChecks) @@ -2844,13 +3143,13 @@ private bool IsIgnoredType(Type type, Func registrationPredicate) // TODO - find a better way to remove "system" types from the auto registration var ignoreChecks = new List>() { - t => t.FullName.StartsWith("System.", StringComparison.InvariantCulture), - t => t.FullName.StartsWith("Microsoft.", StringComparison.InvariantCulture), - t => t.IsPrimitive, + t => t.FullName.StartsWith("System.", StringComparison.Ordinal), + t => t.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + t => t.IsPrimitive(), #if !UNBOUND_GENERICS_GETCONSTRUCTORS - t => t.IsGenericTypeDefinition, + t => t.IsGenericTypeDefinition(), #endif - t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface || t.IsAbstract), + t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface() || t.IsAbstract()), }; if (registrationPredicate != null) @@ -2908,7 +3207,11 @@ private void RemoveRegistration(TypeRegistration typeRegistration) private ObjectFactoryBase GetDefaultObjectFactory(Type registerType, Type registerImplementation) { - if (registerType.IsInterface || registerType.IsAbstract) + //#if NETFX_CORE + // if (registerType.GetTypeInfo().IsInterface() || registerType.GetTypeInfo().IsAbstract()) + //#else + if (registerType.IsInterface() || registerType.IsAbstract()) + //#endif return new SingletonFactory(registerType, registerImplementation); return new MultiInstanceFactory(registerType, registerImplementation); @@ -2961,7 +3264,7 @@ private bool CanResolveInternal(TypeRegistration registration, NamedParameterOve // Attempt unregistered construction if possible and requested // If we cant', bubble if we have a parent - if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) return (GetBestConstructor(checkType, parameters, options) != null) ? true : (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; // Bubble resolution up the container tree if we have a parent @@ -2973,7 +3276,7 @@ private bool CanResolveInternal(TypeRegistration registration, NamedParameterOve private bool IsIEnumerableRequest(Type type) { - if (!type.IsGenericType) + if (!type.IsGenericType()) return false; Type genericType = type.GetGenericTypeDefinition(); @@ -2986,7 +3289,7 @@ private bool IsIEnumerableRequest(Type type) private bool IsAutomaticLazyFactoryRequest(Type type) { - if (!type.IsGenericType) + if (!type.IsGenericType()) return false; Type genericType = type.GetGenericTypeDefinition(); @@ -2996,11 +3299,19 @@ private bool IsAutomaticLazyFactoryRequest(Type type) return true; // 2 parameter func with string as first parameter (name) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string))) + //#else if ((genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))) + //#endif return true; // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string) && type.GetTypeInfo().GenericTypeArguments[1] == typeof(IDictionary))) + //#else if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif return true; return false; @@ -3043,7 +3354,7 @@ private object ResolveInternal(TypeRegistration registration, NamedParameterOver #if RESOLVE_OPEN_GENERICS // Attempt container resolution of open generic - if (registration.Type.IsGenericType) + if (registration.Type.IsGenericType()) { var openTypeRegistration = new TypeRegistration(registration.Type.GetGenericTypeDefinition(), registration.Name); @@ -3117,9 +3428,9 @@ private object ResolveInternal(TypeRegistration registration, NamedParameterOver return GetIEnumerableRequest(registration.Type); // Attempt unregistered construction if possible and requested - if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) { - if (!registration.Type.IsAbstract && !registration.Type.IsInterface) + if (!registration.Type.IsAbstract() && !registration.Type.IsInterface()) return ConstructType(null, registration.Type, parameters, options); } @@ -3130,18 +3441,26 @@ private object ResolveInternal(TypeRegistration registration, NamedParameterOver #if EXPRESSIONS private object GetLazyAutomaticFactoryRequest(Type type) { - if (!type.IsGenericType) + if (!type.IsGenericType()) return null; Type genericType = type.GetGenericTypeDefinition(); + //#if NETFX_CORE + // Type[] genericArguments = type.GetTypeInfo().GenericTypeArguments.ToArray(); + //#else Type[] genericArguments = type.GetGenericArguments(); + //#endif // Just a func if (genericType == typeof(Func<>)) { Type returnType = genericArguments[0]; + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => !mi.GetParameters().Any()); + //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { }); + //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod); @@ -3156,7 +3475,11 @@ private object GetLazyAutomaticFactoryRequest(Type type) { Type returnType = genericArguments[1]; + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 1 && mi.GetParameters()[0].GetType() == typeof(String)); + //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String) }); + //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); ParameterExpression[] resolveParameters = new ParameterExpression[] { Expression.Parameter(typeof(String), "name") }; @@ -3168,14 +3491,22 @@ private object GetLazyAutomaticFactoryRequest(Type type) } // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GenericTypeArguments[0] == typeof(string) && type.GenericTypeArguments[1] == typeof(IDictionary))) + //#else if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif { Type returnType = genericArguments[2]; var name = Expression.Parameter(typeof(string), "name"); var parameters = Expression.Parameter(typeof(IDictionary), "parameters"); + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 2 && mi.GetParameters()[0].GetType() == typeof(String) && mi.GetParameters()[1].GetType() == typeof(NamedParameterOverloads)); + //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String), typeof(NamedParameterOverloads) }); + //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, name, Expression.Call(typeof(NamedParameterOverloads), "FromIDictionary", null, parameters)); @@ -3190,7 +3521,11 @@ private object GetLazyAutomaticFactoryRequest(Type type) #endif private object GetIEnumerableRequest(Type type) { + //#if NETFX_CORE + // var genericResolveAllMethod = this.GetType().GetGenericMethod("ResolveAll", type.GenericTypeArguments, new[] { typeof(bool) }); + //#else var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) }); + //#endif return genericResolveAllMethod.Invoke(this, new object[] { false }); } @@ -3207,7 +3542,11 @@ private bool CanConstruct(ConstructorInfo ctor, NamedParameterOverloads paramete var isParameterOverload = parameters.ContainsKey(parameter.Name); - if (parameter.ParameterType.IsPrimitive && !isParameterOverload) + //#if NETFX_CORE + // if (parameter.ParameterType.GetTypeInfo().IsPrimitive && !isParameterOverload) + //#else + if (parameter.ParameterType.IsPrimitive() && !isParameterOverload) + //#endif return false; if (!isParameterOverload && !CanResolveInternal(new TypeRegistration(parameter.ParameterType), NamedParameterOverloads.Default, options)) @@ -3222,19 +3561,33 @@ private ConstructorInfo GetBestConstructor(Type type, NamedParameterOverloads pa if (parameters == null) throw new ArgumentNullException("parameters"); - if (type.IsValueType) + //#if NETFX_CORE + // if (type.GetTypeInfo().IsValueType) + //#else + if (type.IsValueType()) + //#endif return null; // Get constructors in reverse order based on the number of parameters // i.e. be as "greedy" as possible so we satify the most amount of dependencies possible var ctors = this.GetTypeConstructors(type); - return ctors.FirstOrDefault(ctor => this.CanConstruct(ctor, parameters, options)); + foreach (var ctor in ctors) + { + if (this.CanConstruct(ctor, parameters, options)) + return ctor; + } + + return null; } private IEnumerable GetTypeConstructors(Type type) { + //#if NETFX_CORE + // return type.GetTypeInfo().DeclaredConstructors.OrderByDescending(ctor => ctor.GetParameters().Count()); + //#else return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Count()); + //#endif } private object ConstructType(Type requestedType, Type implementationType, ResolveOptions options) @@ -3257,15 +3610,14 @@ private object ConstructType(Type requestedType, Type implementationType, Constr var typeToConstruct = implementationType; #if RESOLVE_OPEN_GENERICS - if (implementationType.IsGenericTypeDefinition) + if (implementationType.IsGenericTypeDefinition()) { - if (requestedType == null || !requestedType.IsGenericType || !requestedType.GetGenericArguments().Any()) + if (requestedType == null || !requestedType.IsGenericType() || !requestedType.GetGenericArguments().Any()) throw new TinyIoCResolutionException(typeToConstruct); typeToConstruct = typeToConstruct.MakeGenericType(requestedType.GetGenericArguments()); } #endif - if (constructor == null) { // Try and get the best constructor that we can construct @@ -3309,7 +3661,12 @@ private object ConstructType(Type requestedType, Type implementationType, Constr try { +#if USE_OBJECT_CONSTRUCTOR + var constructionDelegate = CreateObjectConstructionDelegateWithCache(constructor); + return constructionDelegate.Invoke(args); +#else return constructor.Invoke(args); +#endif } catch (Exception ex) { @@ -3317,11 +3674,50 @@ private object ConstructType(Type requestedType, Type implementationType, Constr } } +#if USE_OBJECT_CONSTRUCTOR + private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) + { + ObjectConstructor objectConstructor; + if (_ObjectConstructorCache.TryGetValue(constructor, out objectConstructor)) + return objectConstructor; + + // We could lock the cache here, but there's no real side + // effect to two threads creating the same ObjectConstructor + // at the same time, compared to the cost of a lock for + // every creation. + var constructorParams = constructor.GetParameters(); + var lambdaParams = Expression.Parameter(typeof(object[]), "parameters"); + var newParams = new Expression[constructorParams.Length]; + + for (int i = 0; i < constructorParams.Length; i++) + { + var paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i)); + + newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType); + } + + var newExpression = Expression.New(constructor, newParams); + + var constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams); + + objectConstructor = (ObjectConstructor)constructionLambda.Compile(); + + _ObjectConstructorCache[constructor] = objectConstructor; + return objectConstructor; + } +#endif + private void BuildUpInternal(object input, ResolveOptions resolveOptions) { + //#if NETFX_CORE + // var properties = from property in input.GetType().GetTypeInfo().DeclaredProperties + // where (property.GetMethod != null) && (property.SetMethod != null) && !property.PropertyType.GetTypeInfo().IsValueType + // select property; + //#else var properties = from property in input.GetType().GetProperties() - where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType + where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType() select property; + //#endif foreach (var property in properties) { @@ -3361,24 +3757,46 @@ private IEnumerable ResolveAllInternal(Type resolveType, bool includeUnn private static bool IsValidAssignment(Type registerType, Type registerImplementation) { - if (!registerType.IsGenericTypeDefinition) + //#if NETFX_CORE + // var registerTypeDef = registerType.GetTypeInfo(); + // var registerImplementationDef = registerImplementation.GetTypeInfo(); + + // if (!registerTypeDef.IsGenericTypeDefinition) + // { + // if (!registerTypeDef.IsAssignableFrom(registerImplementationDef)) + // return false; + // } + // else + // { + // if (registerTypeDef.IsInterface()) + // { + // if (!registerImplementationDef.ImplementedInterfaces.Any(t => t.GetTypeInfo().Name == registerTypeDef.Name)) + // return false; + // } + // else if (registerTypeDef.IsAbstract() && registerImplementationDef.BaseType() != registerType) + // { + // return false; + // } + // } + //#else + if (!registerType.IsGenericTypeDefinition()) { if (!registerType.IsAssignableFrom(registerImplementation)) return false; } else { - if (registerType.IsInterface) + if (registerType.IsInterface()) { if (!registerImplementation.FindInterfaces((t, o) => t.Name == registerType.Name, null).Any()) return false; } - else if (registerType.IsAbstract && registerImplementation.BaseType != registerType) + else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType) { return false; } } - + //#endif return true; } @@ -3400,4 +3818,64 @@ public void Dispose() #endregion } -} \ No newline at end of file + +} + +// reverse shim for WinRT SR changes... +#if !NETFX_CORE +namespace System.Reflection +{ + public static class ReverseTypeExtender + { + public static bool IsClass(this Type type) + { + return type.IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.IsAbstract; + } + + public static bool IsInterface(this Type type) + { + return type.IsInterface; + } + + public static bool IsPrimitive(this Type type) + { + return type.IsPrimitive; + } + + public static bool IsValueType(this Type type) + { + return type.IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.IsGenericType; + } + + public static bool IsGenericParameter(this Type type) + { + return type.IsGenericParameter; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.IsGenericTypeDefinition; + } + + public static Type BaseType(this Type type) + { + return type.BaseType; + } + + public static Assembly Assembly(this Type type) + { + return type.Assembly; + } + } +} +#endif \ No newline at end of file diff --git a/FluentAutomation/ActionSyntaxProvider.cs b/FluentAutomation/ActionSyntaxProvider.cs index bfcf9a9..06baa03 100644 --- a/FluentAutomation/ActionSyntaxProvider.cs +++ b/FluentAutomation/ActionSyntaxProvider.cs @@ -184,6 +184,26 @@ public void WaitUntil(Expression conditionAction) { this.commandProvider.WaitUntil(conditionAction); } + + public void WaitUntil(Expression> conditionFunc, int secondsToWait) + { + this.WaitUntil(conditionFunc, TimeSpan.FromSeconds(secondsToWait)); + } + + public void WaitUntil(Expression> conditionFunc, TimeSpan timeout) + { + this.commandProvider.WaitUntil(conditionFunc, timeout); + } + + public void WaitUntil(Expression conditionAction, int secondsToWait) + { + this.WaitUntil(conditionAction, TimeSpan.FromSeconds(secondsToWait)); + } + + public void WaitUntil(Expression conditionAction, TimeSpan timeout) + { + this.commandProvider.WaitUntil(conditionAction, timeout); + } public void Upload(string selector, string fileName) { diff --git a/FluentAutomation/BaseFluentTest.cs b/FluentAutomation/BaseFluentTest.cs index 765a1e7..6214978 100644 --- a/FluentAutomation/BaseFluentTest.cs +++ b/FluentAutomation/BaseFluentTest.cs @@ -8,28 +8,17 @@ namespace FluentAutomation { public class BaseFluentTest : IDisposable { - private TinyIoC.TinyIoCContainer container = null; - public TinyIoC.TinyIoCContainer Container - { - get - { - if (container == null) - { - container = new TinyIoC.TinyIoCContainer(); - if (Settings.MinimizeAllWindowsOnTestStart) Win32Magic.MinimizeAllWindows(); - } - - return container; - } - } - - protected IDisposable syntaxProvider = null; + public ISyntaxProvider SyntaxProvider { get; set; } public void Dispose() { try { - if (this.syntaxProvider != null) this.syntaxProvider.Dispose(); + if (FluentSession.Current == null && this.SyntaxProvider != null) + { + this.SyntaxProvider.Dispose(); + } + if (Settings.MinimizeAllWindowsOnTestStart) Win32Magic.RestoreAllWindows(); } catch { }; diff --git a/FluentAutomation/EmbeddedResources.cs b/FluentAutomation/EmbeddedResources.cs index 016482d..19be866 100644 --- a/FluentAutomation/EmbeddedResources.cs +++ b/FluentAutomation/EmbeddedResources.cs @@ -20,24 +20,50 @@ public static string WorkingDirectory } } - public static void UnpackFromAssembly(string resourceFileName, Assembly assembly) + public static string FileExists(string fileName) { - UnpackFromAssembly(resourceFileName, resourceFileName, assembly); + var localPath = Path.Combine(WorkingDirectory, fileName); + if (File.Exists(fileName)) return localPath; + + var path = Environment.GetEnvironmentVariable("PATH"); + if (path == null) return string.Empty; + + var invalidChars = Path.GetInvalidPathChars(); + var pathValues = path.Split(Path.PathSeparator); + foreach (var pathValue in pathValues) + { + if (pathValue.IndexOfAny(invalidChars) == -1) + { + var filePath = Path.Combine(pathValue, fileName); + if (File.Exists(filePath)) + { + return filePath; + } + } + } + + return string.Empty; } - public static void UnpackFromAssembly(string resourceFileName, string outputFileName, Assembly assembly) + public static string UnpackFromAssembly(string resourceFileName, Assembly assembly) { + return UnpackFromAssembly(resourceFileName, resourceFileName, assembly); + } + + public static string UnpackFromAssembly(string resourceFileName, string outputFileName, Assembly assembly) + { + var filePath = FileExists(outputFileName); + if (filePath != string.Empty) return filePath; + var resourceName = assembly.GetManifestResourceNames().FirstOrDefault(x => x.EndsWith(resourceFileName)); - var outputName = Path.Combine(WorkingDirectory, outputFileName); + var resourceStream = assembly.GetManifestResourceStream(resourceName); + var resourceBytes = new byte[(int)resourceStream.Length]; - if (!File.Exists(outputName)) - { - var resourceStream = assembly.GetManifestResourceStream(resourceName); - var resourceBytes = new byte[(int)resourceStream.Length]; + var tmpPath = Path.Combine(Path.GetTempPath(), outputFileName); + resourceStream.Read(resourceBytes, 0, resourceBytes.Length); + File.WriteAllBytes(tmpPath, resourceBytes); - resourceStream.Read(resourceBytes, 0, resourceBytes.Length); - File.WriteAllBytes(outputName, resourceBytes); - } + return tmpPath; } } -} +} \ No newline at end of file diff --git a/FluentAutomation/FluentAutomation.csproj b/FluentAutomation/FluentAutomation.csproj index d5a3fdb..1296f30 100644 --- a/FluentAutomation/FluentAutomation.csproj +++ b/FluentAutomation/FluentAutomation.csproj @@ -46,6 +46,7 @@ + diff --git a/FluentAutomation/FluentSession.cs b/FluentAutomation/FluentSession.cs new file mode 100644 index 0000000..920d07c --- /dev/null +++ b/FluentAutomation/FluentSession.cs @@ -0,0 +1,88 @@ +using FluentAutomation.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FluentAutomation +{ + public class FluentSession : IDisposable + { + internal static FluentSession Current { get; set; } + + public FluentSession() + { + if (FluentSession.Current != null) + { + this.Container = FluentSession.Current.Container; + this.SyntaxProviderRegisterOptions = FluentSession.Current.SyntaxProviderRegisterOptions; + } + else + { + this.Container = new TinyIoC.TinyIoCContainer(); + } + + if (Settings.MinimizeAllWindowsOnTestStart) Win32Magic.MinimizeAllWindows(); + } + + internal TinyIoC.TinyIoCContainer.RegisterOptions SyntaxProviderRegisterOptions = null; + + internal bool HasBootstrappedTypes = false; + + public TinyIoC.TinyIoCContainer Container { get; private set; } + + public void RegisterSyntaxProvider() where T : ISyntaxProvider + { + if (this.SyntaxProviderRegisterOptions == null) + this.SyntaxProviderRegisterOptions = this.Container.Register(typeof(ISyntaxProvider), typeof(T)); + } + + public ISyntaxProvider GetSyntaxProvider() + { + return this.Container.Resolve(); + } + + public void BootstrapTypeRegistration(Action containerAction) + { + if (FluentSession.Current == null) + { + containerAction(this.Container); + } + else if (FluentSession.Current.HasBootstrappedTypes == false) + { + containerAction(this.Container); + FluentSession.Current.HasBootstrappedTypes = true; + } + } + + public static void EnableStickySession() + { + FluentSession.Current = new FluentSession(); + if (FluentSession.Current.SyntaxProviderRegisterOptions == null) + FluentSession.Current.RegisterSyntaxProvider(); + + FluentSession.Current.SyntaxProviderRegisterOptions.AsSingleton(); + } + + public static void DisableStickySession() + { + FluentSession.Current.SyntaxProviderRegisterOptions.AsMultiInstance(); + FluentSession.Current = null; + } + + public static void SetStickySession(FluentSession session) + { + FluentSession.Current = session; + FluentSession.Current.SyntaxProviderRegisterOptions.AsSingleton(); + } + + public void Dispose() + { + try + { + this.GetSyntaxProvider().Dispose(); + } + catch (Exception) { } + } + } +} diff --git a/FluentAutomation/FluentTest.cs b/FluentAutomation/FluentTest.cs index 9c4fb0f..961b894 100644 --- a/FluentAutomation/FluentTest.cs +++ b/FluentAutomation/FluentTest.cs @@ -19,15 +19,29 @@ public INativeActionSyntaxProvider I { get { - var provider = syntaxProvider as INativeActionSyntaxProvider; + var provider = SyntaxProvider as INativeActionSyntaxProvider; if (provider == null || provider.IsDisposed()) { - // register types - FluentAutomation.Settings.Registration(this.Container); - syntaxProvider = this.Container.Resolve(); + this.Session.BootstrapTypeRegistration(FluentAutomation.Settings.Registration); + SyntaxProvider = this.Session.GetSyntaxProvider(); } - return syntaxProvider as INativeActionSyntaxProvider; + return SyntaxProvider as INativeActionSyntaxProvider; + } + } + + private FluentSession session = null; + public FluentSession Session + { + get + { + if (session == null) + { + session = new FluentSession(); + session.RegisterSyntaxProvider(); + } + + return session; } } } diff --git a/FluentAutomation/Properties/AssemblyGlobal.cs b/FluentAutomation/Properties/AssemblyGlobal.cs index 3a9eefe..cac436f 100644 --- a/FluentAutomation/Properties/AssemblyGlobal.cs +++ b/FluentAutomation/Properties/AssemblyGlobal.cs @@ -9,4 +9,4 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("2.1.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("2.2.0.0")] \ No newline at end of file