diff --git a/.gitignore b/.gitignore index b6e59339..f7855cdc 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ x64/ build/ [Bb]in/ [Oo]bj/ +.vs/ # MSTest test Results [Tt]est[Rr]esult*/ @@ -130,7 +131,7 @@ publish/ # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ +packages/ # Windows Azure Build Output csx diff --git a/README.md b/README.md index bc2bb2ea..3e572d1e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,25 @@ -## RevitTestFramework +# RevitTestFramework -The Revit Test Framework (RTF) allows you to conduct remote unit testing on Revit. RTF takes care of creating a journal file for running revit which can specify a model to start Revit, and a specific test or fixture of tests to Run. You can even specify a model to open before testing and RTF will do that as well. +The Revit Test Framework (RTF) allows you to conduct remote unit testing on Revit. RTF takes care of creating a journal file for running revit which can specify a model to start Revit, and a specific test or fixture of tests to run. You can even specify a model to open before testing and RTF will do that as well. + +[![Build status](https://ci.appveyor.com/api/projects/status/oqd23280efmaimmm/branch/mark/Revit2019?svg=true)](https://ci.appveyor.com/project/marchello2000/revittestframework/branch/mark/Revit2019) +[![NuGet](https://img.shields.io/nuget/v/revittestframework.svg)](https://www.nuget.org/packages/revittestframework) ## Applications -##### RevitTestFrameworkConsole.exe +There are two ways to run RTF: +* **RevitTestFrameworkConsole.exe** + A Console application to run test from a command line +* **RevitTestFrameworkGUI.exe** + A GUI application that allows easy selection of tests to run and check results + +Both options run the tests in the same way + +### RevitTestFrameworkConsole.exe A console application which allows running RTF without a user interface. If you'd like to learn more about the command line options for RTF, you can simply type "RevitTestFrameworkConsole -h" and you'll get something like this: ``` - Options: +Options: + --dir=[VALUE] The full path to the working directory. The working directory is the directory in which RTF will generate the journal and the addin to Run Revit. Revit's run-by-journal capability requires that all addins which need to be loaded are in the same directory as the journal file. So, if you're testing other addins on top of Revit using RTF, you'll need to put those addins in whatever directory you specify as the working directory. -a, --assembly=[VALUE] The full path to the assembly containing your tests. -r, --results=[VALUE] This is the full path to an .xml file that will contain the results. @@ -21,38 +33,66 @@ A console application which allows running RTF without a user interface. If you' --dry Conduct a dry run. (OPTIONAL) -x, --clean Cleanup journal files after test completion. (OPTIONAL) --continuous Run all selected tests in one Revit session. (OPTIONAL) + --groupByModel Run tests with same model without reopening the model for faster execution, requires --continuous. (OPTIONAL) --time The time, in milliseconds, after which RTF will close the testing process automatically. (OPTIONAL) -d, --debug Should RTF attempt to attach to a debugger?. (OPTIONAL) -h, --help Show this message and exit. (OPTIONAL) ``` -##### RevitTestFrameworkGUI.exe -Provides a visual interface for you to choose tests from a treeview and to visualize the results of the tests as they are run. The same settings provided in the command line argument help above are available in the UI. The UI also allows you to save your testing session. +As an example, the following command: +``` +RevitTestFrameworkConsole.exe --dir C:\MyTestDir -a MyTest.dll -r MyTestResults.xml -revit:"C:\Program Files\Autodesk\Revit 2019\Revit.exe" --continuous +``` +will execute all tests in `MyTest.dll` located in `C:\MyTestDir` and place all results in `MyTestResults.xml` (in the same folder). It will use Revit 2019 as specified and will run all tests without shutting down Revit. -The input fields to set the test assembly, the working directory, and the results file, as well as the tree view where available tests are displayed, support dragging and dropping of files and folders. +The results of the run as well as any `Console.WriteLine` or `Console.Error.WriteLine` from test will be shown in the command window and written ot the results xml file, e.g.: -![Image](https://user-images.githubusercontent.com/3942418/28271251-0822ae5c-6ad6-11e7-8028-4f2f5c03823e.png) +``` +Reading assembly: D:\MyTestDir\MyTest.dll +Loaded test: Name: UnitTest1, Model Path: D:\MyTestDir\MyModel1.rvt (D:\MyTestDir\MyModel1.rvt) +Loaded test: Name: UnitTest2, Model Path: D:\MyTestDir\MyModel2.rvt (D:\MyTestDir\MyModel2.rvt) +Running D:\Projects\UpCodes\upcodes_revit\Tests\2019\bin\Debug\RTF_Batch_Test.txt +Running UnitTest1 in BasicTests +Success: UnitTest1 in BasicTests + +Running UnitTest2 in BasicTests +Success: UnitTest2 in BasicTests + +Waiting for Revit to terminate +WARNING: One or more journal files could not be deleted. +``` -`File` - For saving and loading your local RTF config. +The warning about some journal files not being deleted can be safely ignored. -`Test Assembly` - Path to assembly containing the tests to run. E.g. RevitNodesTests.dll, RevitSystemTests.dll, RevitServicesTests.dll. +### RevitTestFrameworkGUI.exe +Provides a visual interface for you to choose tests from a treeview and to visualize the results of the tests as they are run. The same settings provided in the command line argument help above are available in the UI. The UI also allows you to save your testing session. -`Results File Path` - Path to the xml file containing the tests results. If it's an existing file, RTF will replace it. +The input fields to set the test assembly, the working directory, and the results file, as well as the tree view where available tests are displayed, support dragging and dropping of files and folders. -`Working Directory` - Path to where the testing Revit files are. +RevitTestFrameworkGUI.exe can also take an argument for the test assembly path and will automatically fill all values on start so you can just hit `Run`. -`Additional Resolution Directories` - Path to find Dynamo Core location when it is not set in the `Dynamo.Config` in DynamoRevit bin folder. +![RTF](images/RTF_UI.PNG) +`File` - For saving and loading your local RTF config. +`Test Assembly` - Path to assembly containing the tests to run. E.g. RevitNodesTests.dll, RevitSystemTests.dll, RevitServicesTests.dll. +`Results File Path` - Path to the xml file containing the tests results. If it's an existing file, RTF will replace it. +`Working Directory` - Path to where the testing Revit files are. +`Additional Resolution Directories` - Path to find Dynamo Core location when it is not set in the `Dynamo.Config` in DynamoRevit bin folder. `Debug` - Check this if you decide to launch a debug session. +`Timeout` - in milliseconds, the maximum time for a test to run. If a test doesn't finish in this time, Revit will be forcefully terminated and new test session without the offending test will start (the offending test will be marked as `timedout`). +`Continuous` - same as above, run tests without restarting Revit for each test. +`GroupByModel` - same as above, run tests with the same model without reopening the model. ## Results -The output file from a test run is an nunit-formatted results file compatible with many CI systems. +The output file from a test run is an nunit-formatted results file compatible with many CI systems. +For documentation how to use RTF inside a CI system, see [this guide](docs/using_with_ci.md) ## Revit Versions This repo maintains branches to track the two most recently released versions of Revit and one un-released version of Revit. When new versions of Revit are released, branches tracking the oldest version of Revit supported will no longer be maintained. For example, when Revit 2016 is released, the Revit 2014 branch of RTF will no longer be maintained. -When testing, you should run the version of RTF corresponding to the version of Revit you are running. This will ensure that tests you have created, based on one Revit API, will correspond to the version of the API running on Revit. +When testing, you should run the version of RTF corresponding to the version of Revit you are running. This will ensure that tests you have created, based on one Revit API, will correspond to the version of the API running on Revit. +However, the current version of RTF works and can be used to test within Revit 2017, 2018, and 2019. ## License diff --git a/appveyor.yml.deleteme b/appveyor.yml.deleteme new file mode 100644 index 00000000..98977a19 --- /dev/null +++ b/appveyor.yml.deleteme @@ -0,0 +1,36 @@ +version: 1.19.{build} +pull_requests: + do_not_increment_build_number: true +branches: + only: + - mark/Revit2019 +skip_tags: true +image: Visual Studio 2017 +configuration: Release +assembly_info: + patch: true + file: '**\AssemblyInfo.*' + assembly_version: '{version}' + assembly_file_version: '{version}' + assembly_informational_version: '{version}' +before_build: +- cmd: nuget restore .\src\ +build: + project: .\src\RevitTestFramework.sln + verbosity: minimal +after_build: +- cmd: nuget pack src\RevitTestFramework.nuspec -Version %APPVEYOR_BUILD_VERSION% +test: + assemblies: + only: + - .\bin\AnyCPU\Release\RunnerTests.dll +artifacts: +- path: RevitTestFramework*.nupkg + name: nupkg +deploy: +- provider: NuGet + api_key: + secure: cN7zY+VTH8QH103SZn3xen5q3ZNqDvB4a5JKcYTGzBfnFRzCn/kldBPip6ENyCFF + artifact: nupkg + on: + branch: mark/Revit2019 \ No newline at end of file diff --git a/docs/using_with_ci.md b/docs/using_with_ci.md new file mode 100644 index 00000000..74890c06 --- /dev/null +++ b/docs/using_with_ci.md @@ -0,0 +1,128 @@ +# Configuring CI to use RTF + +This doc describes how to configure a CI system and use RevitTestFramework to run Revit tests on the CI machine. +This document will describe setup for Microsoft Visual Studio and AWS EC2, however, the general instructions will work on other systems, e.g. Jenkins, TeamCity, etc. + +1. **Setup Visual Studio Team Services** +Visual Studio Team Services serves as a master in our CI. You can also use Jenkins for this purpose as well. +You will need to host your own machine (steps below) for actually executing the build/test steps as that machine will have to have Revit installed (default VSTS machines, naturally, do not have it installed) + 1. Create an account if you don't have one yet + 1. Set up an agent pool (we have 2 pools one for general build/test and one for UI tests) + 1. Setup a new build pipeline: + 1. As an example, you can use one pipeline for build and one pipeline for UI tests + 1. Connect to your code repository (git/bitbucket/etc), this will popup oauth + 1. Specify an agent pool to run on + An agent pool is a pool of machines that can service a paricular pipeline. Since, in this example, we have 2 pipelines, we will use the `Default` pool for one of them and create a new one, `UI Tests` for the Revit UI tests pipeline + 1. Define the pipeline + These are steps that the build agent will perform to execute the tests, as an example, here are the steps for the pipeline we use: + 1. Get sources + 2. VsTest Platform Installer + 3. Build binaries (we use a PowerShell script to perform the build, but you can use the default MSBuild step) + 4. Run Revit UI Tests + We use a PowerShell script here as well, which essentially executes our RTF tests, e.g.: + ``` + RevitTestFrameworkConsole.exe --dir .\bin\Release -a MyTest.dll -r .\MyTestResults.xml -revit:"C:\Program Files\Autodesk\Revit 2019\Revit.exe" --continuous + ``` + 5. Publish Test Results + This steps takes the XML file produced by RTF and publishes it so that VSTS understands which tests pass and which fail. + Select *NUnit* for Test result format and your test result file, e.g. `.\MyTestResults.xml` + 1. At this point you can also configure triggers for each build pipeline (e.g. a pull request, or a merge into a specific branch, etc) + +1. **Setup the build server** +Now we setup a build machine that will connect to VSTS above and execute the build pipeline + 1. Decide where to host the build machine (AWS/Azure/local/etc) and what Windows variant you want to use. We use AWS and run our CI on Windows Server 2016. + 1. Spin up a new instance, I recommend at least 16GB of RAM and 4 processors with ~150gb of storage (`m5.xlarge` on AWS) + *Security note:* Since you will need to remote into this machine, it needs to be open to the world. To mitigate security risks: + * make sure you have a strong password (or use automatically generated AWS password) + * limit the IPs that are allowed to access this machine to the IP of your office (you can use whatsmyip.org) + 1. Remote into your new instance (as `localhost\Administrator`) + 1. Create a new **non-admin** account (e.g. `vsagent`) on the machine (make sure it's a strong password and you have it saved somewhere safe!) + 1. Setup some convenience stuff + * Create a PowerShell shortcut on the desktop + * Create a shortcut on the desktop to launch CMD as `vsagent` user, e.g. a shortcut to: + `C:\tools\PsExec.exe -accepteula -w c:\agent -i -u vsagent cmd.exe` + This will allow you to run programs as `vsagent` user while being logged in as `Administrator` + 1. Download and install necessary software on the machine: + *Note:* you will not be able to use IE on the remote machine (if it's Windows Server) because it is locked down. I don't recommend removing the lockdown or installing some other browsers - this machine should be secure! + I recommend getting the link to each download on your personal computer, then pasting that link into a PowerShell on the remote, e.g.: + ```PowerShell + # Disable progress bar: makes downloads ~50x faster + $ProgressPreference = "SilentlyContinue" + + # Download the file + Invoke-WebRequest -OutFile + ``` + 1. [Git](https://git-scm.com/download/win) (or other source control software needed to connect to your source code repository, use defaults) + 1. [Visual Studio](https://www.visualstudio.com/downloads/), scroll down to [VS Build tools](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15) + Make sure to select the following components to be installed (in addition to defaults): + * .NET for desktop + * Windows SDK (latest version) + * Click-once tools + 1. [Wix](https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311.exe) or whatever other tool you need to build your installer/setup package. + * If you use Wix, you will need to enable .NET 3.5 on the server, you can do this in the *Turn on Optional Features* control panel. + You will probably need to reboot after this. + * Install Wix + * You will need to add the following system-wide environment variables: + ``` + WixCATargetsPath = "C:\Program Files (x86)\WiX Toolset v3.11\SDK\wix.ca.targets" + WixTargetsPath = "C:\Program Files (x86)\MSBuild\Microsoft\WiX\v3.x\wix.targets" + ``` + 1. [PsTools](https://docs.microsoft.com/en-us/sysinternals/downloads/pstools), simply extract the .zip into a folder, e.g. `c:\tools` + 1. VsAgent click the *Download Agent* button on your VSTS account webpage. To install it, just unzip it to a new folder: `c:\agent` + 1. Revit (we use 2019.1 for our CI) + * Once installed, you will need to run the Revit once to register it and dismiss any first run prompts (e.g. EULA, etc). This will require allowing Autodesk registration site to bypass IE security settings: + * Open an interactive session to the `vsagent` user with `psexec` (or the shortcut we created above): + `C:\tools\PsExec.exe -i -u vsagent cmd.exe` + * In the CMD for `vsagent`, open Internet Explorer: + `c:\Program Files\Internet Explorer\iexplore.exe` + * In IE, click on the gear/*Tools* icon, *Internet Options*, *Security*, *Trusted Sites*, *Sites*, and add `https://registeronce.autodesk.com` to the list. + * Close IE + * Now, from the same command window, run Revit, e.g. + `C:\Program Files\Autodesk\Revit 2019\Revit.exe` + * Accept all prompts, login, whatever you need to do, then close Revit. + * For good measure, run Revit again and make sure no prompt show up - as they will cause issues with RTF + 1. At this point we have a machine that can run our CI/Revit UI tests, let's make a snapshot (AMI) of it so we can easily spin up as many of these as we want. For AWS/AMI: + * (on the remote machine) Open `Ec2LaunchSettings` + * Keep all settings, and click *Shutdown with Sysprep* + * In the EC2 console, select the instance and chose *Image* -> *Create Image* from the *Actions* menu (give the image a descriptive name, e.g. `Revit Addin CI`) + * This will take a few minutes. Once done you can use this AMI to spin up as many worker agents as you want. + +1. Spin up a build server and connect it to Visual Studio Team Services + 1. If using an AMI, use that AMI. Optionally, you can skip the AMI step and use the machine instance from above directly (though it's not recommended) + Settings to consider: + * You can assign an AWS role to the machine such that it has credentials to access any private resources you may need (e.g. if you want to deploy data to an S3 bucket) + * You can create a security group which limits remote connection to your IP addresses and assign that security group to the machine + 1. Once the machine is booted up, remote into it. + For credentials, use `localhost\Administrator` and the password you decrypt on AWS console (with the PEM file) + 1. You may want to consider installing code signing certificate at this point if you are signing your Revit Addin (highly recommended) + 1. Open CMD to `vsagent` (see steps above) + 1. Open `certmgr` from CMD + 1. Install the certificate into personal store + 1. Finally, configure the Visual Studio agent, open a PowerShell window and: + ```PowerShell + cd c:\agent + .\config.cmd + + # Follow the prompts + # Server url: your VSTS url, e.g. https://[mybuild].visualstudio.com + # Auth type: PAT + # PAT: (get it from https://[mybuild].visualstudio.com/_usersSettings/tokens) + # Pool: use default for CI and UI Tests for Revit (or whatever pools you created above) + # Agent name: e.g. REVIT-CI-1 + # Work folder: [default value] + # Run as service: NO + # Configure autologon: YES + # Account to use: vsagent [+ your password when prompted] + ``` + Reboot. + 1. Once rebooted you should see your new agent on Visual Studio Team Services webpage + 1. Queue up a new build and see if it works! + +1. Debugging when things go wrong... + In some cases, you just need to remote into the build machine to debug a broken build. + Since the build runs as the `vsagent` account but `vsagent` account can remote into the machine, you will have to follow these steps: + 1. Open AWS console, EC2 instances + 2. Select the instance and click *Connect* + 3. Use the PEM file to decrypt the password + 4. Once connected (you will always connect with `localhost\Administrator` account), open *Task Manager* and click on *Users* then right click on `vsagent` and click connect. + 5. Now you can run and debug on the live machine \ No newline at end of file diff --git a/images/RTF_UI.PNG b/images/RTF_UI.PNG index 852053a1..19f1aa57 100644 Binary files a/images/RTF_UI.PNG and b/images/RTF_UI.PNG differ diff --git a/licenses.txt b/licenses.txt new file mode 100644 index 00000000..5dc3076a --- /dev/null +++ b/licenses.txt @@ -0,0 +1,123 @@ +RevitTestFramework +Copyright 2014 Autodesk + +Licensed under The MIT License; you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://opensource.org/licenses/MIT + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +Prism +The MIT License (MIT) + +Copyright (c) .NET Foundation + +All rights reserved. Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, s +ubject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +Moq +BSD 3-Clause License + +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Ndesk.Options +Copyright NDesk AUTHORS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +NUnit +NUnit License +Copyright � 2002-2012 Charlie Poole +Copyright � 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov +Copyright � 2000-2002 Philip A. Craig + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment (see the following) in the product documentation is + required. + + Portions Copyright � 2002-2009 Charlie Poole or + Copyright � 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or + Copyright � 2000-2002 Philip A. Craig + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/src/Applications/RTFRevit/ConsoleOutInterceptor.cs b/src/Applications/RTFRevit/ConsoleOutInterceptor.cs new file mode 100644 index 00000000..6bad214e --- /dev/null +++ b/src/Applications/RTFRevit/ConsoleOutInterceptor.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Text; + +namespace RTF.Applications +{ + /// + /// Interface to implement to get console/error out text + /// + public interface IConsoleTraceListener + { + /// + /// Called with each complete line received on Console.Out + /// + /// line of text + void OnConsoleOutLine(string text); + + /// + /// Called with each complete line received on Error.Out + /// + /// line of text + void OnErrorOutLine(string text); + } + + /// + /// Helper class to redirect and intercept Console Out and Error Out streams + /// This is used to shuffle all console messages from RTF client to server + /// + public class ConsoleOutInterceptor : IDisposable + { + private LineTextWriter outWriter; + private LineTextWriter errorWriter; + private IConsoleTraceListener listener; + + /// + /// .ctor + /// + /// object that implements + /// which will receive callbacks for each line of output intercepted + public ConsoleOutInterceptor(IConsoleTraceListener listener) + { + outWriter = new LineTextWriter(OnConsole); + errorWriter = new LineTextWriter(OnError); + this.listener = listener; + + Console.SetOut(outWriter); + Console.SetError(errorWriter); + } + + /// + /// Callback for LineTextWriter. + /// Called when a line of text is received from the Console.Out stream + /// + /// line of text + private void OnConsole(string line) + { + listener?.OnConsoleOutLine(line); + } + + /// + /// Callback for LineTextWriter. + /// Called when a line of text is received from the Console.Error stream + /// + /// line of text + private void OnError(string line) + { + listener?.OnErrorOutLine(line); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + outWriter?.Dispose(); + errorWriter?.Dispose(); + + outWriter = null; + errorWriter = null; + } + } + } + + /// + /// TextWriter that produces a callback after each line of text + /// + public class LineTextWriter : TextWriter + { + private StringBuilder pendingText = new StringBuilder(); + private Action callback; + + public LineTextWriter(Action callback) + { + this.callback = callback; + } + + /// + /// Write a char to the pending buffer + /// This is the only overload we need since that's all that Console streams call + /// + public override void Write(char value) + { + base.Write(value); + if (value != '\n') + { + if (value == '\r') + { + string line = GetPendingLine(); + callback?.Invoke(line); + } + else + { + pendingText.Append(value); + } + } + } + + public override Encoding Encoding + { + get + { + return Encoding.UTF8; + } + } + + private string GetPendingLine() + { + string line = pendingText.ToString(); + pendingText.Clear(); + + return line; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + string line = GetPendingLine(); + + if (!string.IsNullOrEmpty(line)) + { + callback?.Invoke(line); + } + } + } + } +} diff --git a/src/Applications/RTFRevit/RTFRevit.csproj b/src/Applications/RTFRevit/RTFRevit.csproj index d6bdd21f..93445bd6 100644 --- a/src/Applications/RTFRevit/RTFRevit.csproj +++ b/src/Applications/RTFRevit/RTFRevit.csproj @@ -3,6 +3,8 @@ + + Debug @@ -14,6 +16,9 @@ RTFRevit 512 + None + + true @@ -35,6 +40,13 @@ false + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\AdWindows.dll + False + + + ..\..\packages\Costura.Fody.3.1.2\lib\net46\Costura.dll + ..\..\..\lib\NUnit\nunit.core.dll @@ -42,12 +54,24 @@ False ..\..\..\lib\NUnit\nunit.core.interfaces.dll - - $(REVITAPI)\RevitAPI.dll + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPI.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIIFC.dll False - - $(REVITAPI)\RevitAPIUI.dll + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIMacros.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIUI.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIUIMacros.dll False @@ -56,6 +80,7 @@ + NUnitResultTypes.xsd @@ -75,8 +100,16 @@ Designer + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + - - - - - - - - - - - + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + - + Text="{Binding Path=TestAssembly, Converter={StaticResource AssemblyPathConverter},Mode=TwoWay}" + IsEnabled="{Binding IsRunning, Converter={StaticResource BoolInverseConverter}}" + TextChanged="UpdateRequired" + PreviewDragOver="OnPreviewDragOver" + AllowDrop="True" + Drop="Assembly_OnDrop" + Grid.Row="0" Grid.Column="1"/> - - - - + + - + TextChanged="UpdateRequired" + PreviewDragOver="OnPreviewDragOver" AllowDrop="True" Drop="Results_OnDrop" + Grid.Column="1" Grid.Row="2"/> - - - - + + - + IsEnabled="{Binding IsRunning, Converter={StaticResource BoolInverseConverter}}" + TextChanged="UpdateRequired" + PreviewDragOver="OnPreviewDragOver" + AllowDrop="True" + Drop="WorkingDir_OnDrop" + Grid.Column="1" Grid.Row="4"/> - - - - + + - + IsEnabled="{Binding IsRunning, Converter={StaticResource BoolInverseConverter}}" + TextChanged="UpdateRequired" + PreviewDragOver="OnPreviewDragOver" + Height="35" + VerticalScrollBarVisibility="Auto" + TextWrapping="Wrap" KeyDown="UIElement_OnKeyDown" LostFocus="UIElement_OnLostFocus" + Grid.Column="1" Grid.Row="6"/> + - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Applications/RevitTestFrameworkGUI/MainWindow.xaml.cs b/src/Applications/RevitTestFrameworkGUI/MainWindow.xaml.cs index 7f930772..1c96ddc9 100644 --- a/src/Applications/RevitTestFrameworkGUI/MainWindow.xaml.cs +++ b/src/Applications/RevitTestFrameworkGUI/MainWindow.xaml.cs @@ -24,16 +24,15 @@ public MainWindow() DataContext = vm; Closing += View_Closing; - Loaded += MainWindow_Loaded; - } - - void MainWindow_Loaded(object sender, RoutedEventArgs e) - { - // When running with a UI, we redirect the console output - // to a text box in the application's interface. var outputter = new TextBoxOutputter(ConsoleTextBlock); Console.SetOut(outputter); + + var args = Environment.GetCommandLineArgs(); + if (args.Length == 2) + { + vm.TestAssembly = args[1]; + } } private void View_Closing(object sender, CancelEventArgs e) @@ -119,5 +118,10 @@ private static void UpdateTbBinding(object sender) be.UpdateSource(); } } + + private void ClearOutput_Click(object sender, RoutedEventArgs e) + { + ConsoleTextBlock.Document.Blocks.Clear(); + } } } diff --git a/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.cs b/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.cs deleted file mode 100644 index ed89c552..00000000 --- a/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.cs +++ /dev/null @@ -1,571 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18051 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -// -// This source code was auto-generated by xsd, Version=4.0.30319.1. -// -namespace Dynamo.NUnit.Tests { - using System.Xml.Serialization; - - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlRootAttribute("test-results", Namespace="", IsNullable=false)] - public partial class resultType { - - private testsuiteType testsuiteField; - - private string nameField; - - private decimal totalField; - - private decimal failuresField; - - private decimal notrunField; - - private string dateField; - - private string timeField; - - //added for compliance with jenkins reader - private decimal errorsField; - - private decimal inconclusiveField; - - private decimal ignoredField; - - private decimal skippedField; - - private decimal invalidField; - - /// - [System.Xml.Serialization.XmlElementAttribute("test-suite")] - public testsuiteType testsuite { - get { - return this.testsuiteField; - } - set { - this.testsuiteField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name { - get { - return this.nameField; - } - set { - this.nameField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal total { - get { - return this.totalField; - } - set { - this.totalField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal failures { - get { - return this.failuresField; - } - set { - this.failuresField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute("not-run")] - public decimal notrun { - get { - return this.notrunField; - } - set { - this.notrunField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string date { - get { - return this.dateField; - } - set { - this.dateField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string time { - get { - return this.timeField; - } - set { - this.timeField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal errors - { - get - { - return this.errorsField; - } - set - { - this.errorsField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal inconclusive - { - get - { - return this.inconclusiveField; - } - set - { - this.inconclusiveField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal ignored - { - get - { - return this.ignoredField; - } - set - { - this.ignoredField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal skipped - { - get - { - return this.skippedField; - } - set - { - this.skippedField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public decimal invalid - { - get - { - return this.invalidField; - } - set - { - this.invalidField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(TypeName="test-suiteType")] - public partial class testsuiteType { - - private categoryType[] categoriesField; - - private resultsType resultsField; - - private string nameField; - - private string descriptionField; - - private string successField; - - private string timeField; - - private string assertsField; - - private string typeField; - - private string resultField; - - private string executedField; - - /// - [System.Xml.Serialization.XmlArrayItemAttribute("category", IsNullable=false)] - public categoryType[] categories { - get { - return this.categoriesField; - } - set { - this.categoriesField = value; - } - } - - /// - public resultsType results { - get { - return this.resultsField; - } - set { - this.resultsField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name { - get { - return this.nameField; - } - set { - this.nameField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string description { - get { - return this.descriptionField; - } - set { - this.descriptionField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string success { - get { - return this.successField; - } - set { - this.successField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string time { - get { - return this.timeField; - } - set { - this.timeField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string asserts { - get { - return this.assertsField; - } - set { - this.assertsField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string type - { - get - { - return this.typeField; - } - set - { - this.typeField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string result - { - get - { - return this.resultField; - } - set - { - this.resultField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string executed - { - get - { - return this.executedField; - } - set - { - this.executedField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class categoryType { - - private string nameField; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name { - get { - return this.nameField; - } - set { - this.nameField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class reasonType { - - private string messageField; - - /// - public string message { - get { - return this.messageField; - } - set { - this.messageField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class failureType { - - private string messageField; - - private string stacktraceField; - - /// - public string message { - get { - return this.messageField; - } - set { - this.messageField = value; - } - } - - /// - [System.Xml.Serialization.XmlElementAttribute("stack-trace")] - public string stacktrace { - get { - return this.stacktraceField; - } - set { - this.stacktraceField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlTypeAttribute(TypeName="test-caseType")] - public partial class testcaseType { - - private categoryType[] categoriesField; - - private object itemField; - - private string nameField; - - private string descriptionField; - - private string successField; - - private string timeField; - - private string executedField; - - private string assertsField; - - private string resultField; - - /// - [System.Xml.Serialization.XmlArrayItemAttribute("category", IsNullable=false)] - public categoryType[] categories { - get { - return this.categoriesField; - } - set { - this.categoriesField = value; - } - } - - /// - [System.Xml.Serialization.XmlElementAttribute("failure", typeof(failureType))] - [System.Xml.Serialization.XmlElementAttribute("reason", typeof(reasonType))] - public object Item { - get { - return this.itemField; - } - set { - this.itemField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name { - get { - return this.nameField; - } - set { - this.nameField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string description { - get { - return this.descriptionField; - } - set { - this.descriptionField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string success { - get { - return this.successField; - } - set { - this.successField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string time { - get { - return this.timeField; - } - set { - this.timeField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string executed { - get { - return this.executedField; - } - set { - this.executedField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string asserts { - get { - return this.assertsField; - } - set { - this.assertsField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string result - { - get - { - return this.resultField; - } - set - { - this.resultField = value; - } - } - } - - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] - [System.SerializableAttribute()] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - public partial class resultsType { - - private object[] itemsField; - - /// - [System.Xml.Serialization.XmlElementAttribute("test-case", typeof(testcaseType))] - [System.Xml.Serialization.XmlElementAttribute("test-suite", typeof(testsuiteType))] - public object[] Items { - get { - return this.itemsField; - } - set { - this.itemsField = value; - } - } - } -} diff --git a/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.xsd b/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.xsd deleted file mode 100644 index df3e76ea..00000000 --- a/src/Applications/RevitTestFrameworkGUI/NUnitResultTypes.xsd +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Applications/RevitTestFrameworkGUI/RevitTestFrameworkGUI.csproj b/src/Applications/RevitTestFrameworkGUI/RevitTestFrameworkGUI.csproj index b69a2bef..f270db0a 100644 --- a/src/Applications/RevitTestFrameworkGUI/RevitTestFrameworkGUI.csproj +++ b/src/Applications/RevitTestFrameworkGUI/RevitTestFrameworkGUI.csproj @@ -3,6 +3,8 @@ + + Debug @@ -16,6 +18,8 @@ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 + + AnyCPU @@ -38,12 +42,18 @@ 4 false + + icon.ico + + + ..\..\packages\Costura.Fody.3.1.2\lib\net46\Costura.dll + ..\..\..\lib\Microsoft.Practices.Prism.dll - - $(REVITAPI)\RevitAddInUtility.dll + + ..\..\packages\Revit-2017.1.1x64-Utilities.1.0.0\lib\net452\RevitAddInUtility.dll @@ -65,9 +75,6 @@ MainWindow.xaml - - NUnitResultTypes.xsd - App.xaml @@ -97,9 +104,7 @@ ResXFileCodeGenerator Resources.Designer.cs - - Designer - + SettingsSingleFileGenerator Settings.Designer.cs @@ -112,7 +117,17 @@ Runner + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/Tests/RunnerTests/RunnerTests.cs b/src/Tests/RunnerTests/RunnerTests.cs index bd78b263..abab0728 100644 --- a/src/Tests/RunnerTests/RunnerTests.cs +++ b/src/Tests/RunnerTests/RunnerTests.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using Autodesk.RevitAddIns; using Moq; using NUnit.Framework; using RTF.Framework; @@ -76,6 +77,18 @@ protected void TestSetup() { Directory.CreateDirectory(workingDir); } + + // Override Revit look up since on CI machine we don't have Revit installed + RunnerSetupData.RevitLookupOverride = () => + { + RevitProduct dummyProduct = (RevitProduct)Activator.CreateInstance(typeof(RevitProduct), true); + + typeof(RevitProduct) + .GetMethod("SetInstallLocation", BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance) + .Invoke(dummyProduct, new object[] { @"C:\dummy\path" }); + + return new List() { dummyProduct }; + }; } [TearDown] diff --git a/src/Tests/RunnerTests/RunnerTests.csproj b/src/Tests/RunnerTests/RunnerTests.csproj index 9f593811..107c2bb0 100644 --- a/src/Tests/RunnerTests/RunnerTests.csproj +++ b/src/Tests/RunnerTests/RunnerTests.csproj @@ -43,10 +43,8 @@ False ..\..\..\lib\NUnit\nunit.framework.dll - - False - $(REVITAPI)\RevitAddInUtility.dll - True + + ..\..\packages\Revit-2017.1.1x64-Utilities.1.0.0\lib\net452\RevitAddInUtility.dll @@ -62,6 +60,9 @@ Runner + + + - + \ No newline at end of file diff --git a/src/Tests/RunnerTests/packages.config b/src/Tests/RunnerTests/packages.config new file mode 100644 index 00000000..e1a1973b --- /dev/null +++ b/src/Tests/RunnerTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Tests/Samples/SampleTests.csproj b/src/Tests/Samples/SampleTests.csproj index e44ec03e..8731d0a4 100644 --- a/src/Tests/Samples/SampleTests.csproj +++ b/src/Tests/Samples/SampleTests.csproj @@ -14,6 +14,7 @@ SampleTests 512 + None true @@ -35,16 +36,32 @@ false + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\AdWindows.dll + False + False ..\..\..\lib\NUnit\nunit.framework.dll - - $(REVITAPI)\RevitAPI.dll + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPI.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIIFC.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIMacros.dll + False + + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIUI.dll False - - $(REVITAPI)\RevitAPIUI.dll + + ..\..\packages\Revit-2017.1.1-x64.Base.2.0.0\lib\net452\RevitAPIUIMacros.dll False @@ -65,6 +82,7 @@ PreserveNewest + @@ -85,4 +103,4 @@ --> - + \ No newline at end of file diff --git a/src/Tests/Samples/packages.config b/src/Tests/Samples/packages.config new file mode 100644 index 00000000..3ca384c0 --- /dev/null +++ b/src/Tests/Samples/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file