diff --git a/.gitignore b/.gitignore index 0be0a878..bc064d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,76 @@ -# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) -[Bb]in/ -[Oo]bj/ +# Files relevant to ScriptCS project -# mstest test results -TestResults - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. +artifacts +/scriptcs_packages # Diff files *.orig +# These are extra items from the original ignore file +# keeping the items here until I submit PR to gitignore.io +*.sln.ide +*.bak +*.ncrunch* +*.Cache +[Ss]tyle[Cc]op.* + +# ----------------------------------------------------------------------- +# Created by https://www.gitignore.io/api/visualstudio + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + # User-specific files *.suo *.user +*.userosscache *.sln.docstates -*.sln.ide + +# User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ +[Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + *_i.c *_p.c +*_i.h *.ilk *.meta *.obj @@ -37,36 +84,73 @@ x64/ *.tli *.tlh *.tmp +*.tmp_proj *.log -*.bak *.vspscc *.vssscc .builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in -_ReSharper* +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml # NCrunch -*.ncrunch* +_NCrunch_* .*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ -# Installshield output folder -[Ee]xpress +# Installshield output folder +[Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ @@ -79,38 +163,155 @@ DocProject/Help/Html2 DocProject/Help/html # Click-Once directory -publish +publish/ # Publish Web Output -*.Publish.xml +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Uncomment the next line to ignore your web deploy settings. +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. +#*.pubxml +*.pubxml.user +*.publishproj -# NuGet Packages Directory -packages +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ -# Windows Azure Build Output -csx +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -[Bb]in -[Oo]bj -sql -TestResults -[Tt]est[Rr]esult* -*.Cache -ClientBin -[Ss]tyle[Cc]op.* +ClientBin/ ~$* +*~ *.dbmdl -Generated_Code #added for RIA/Silverlight projects +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML +UpgradeLog*.htm -artifacts +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +### VisualStudio Patch ### +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. + +# End of https://www.gitignore.io/api/visualstudio +*.DS_Store diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe index c41a0d0d..a34c3675 100644 Binary files a/.nuget/NuGet.exe and b/.nuget/NuGet.exe differ diff --git a/.nuget/packages.config b/.nuget/packages.config index eb41749c..61ff56f9 100644 --- a/.nuget/packages.config +++ b/.nuget/packages.config @@ -1,4 +1,6 @@ - + - - \ No newline at end of file + + + + diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets new file mode 100644 index 00000000..305e736c --- /dev/null +++ b/.paket/Paket.Restore.targets @@ -0,0 +1,300 @@ + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + true + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)..\ + $(PaketRootPath)paket-files\paket.restore.cached + $(PaketRootPath)paket.lock + /Library/Frameworks/Mono.framework/Commands/mono + mono + + $(PaketRootPath)paket.exe + $(PaketToolsPath)paket.exe + "$(PaketExePath)" + $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" + + + <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) + dotnet "$(PaketExePath)" + + + "$(PaketExePath)" + + $(PaketRootPath)paket.bootstrapper.exe + $(PaketToolsPath)paket.bootstrapper.exe + "$(PaketBootStrapperExePath)" + $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" + + + + + true + true + + + + + + + true + $(NoWarn);NU1603;NU1604;NU1605;NU1608 + + + + + /usr/bin/shasum "$(PaketRestoreCacheFile)" | /usr/bin/awk '{ print $1 }' + /usr/bin/shasum "$(PaketLockFilePath)" | /usr/bin/awk '{ print $1 }' + + + + + + + + + + + + + + + + $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) + $([System.IO.File]::ReadAllText('$(PaketLockFilePath)')) + true + false + true + + + + true + + + + + + + + + + + + + + + + $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).paket.references.cached + + $(MSBuildProjectFullPath).paket.references + + $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references + + $(MSBuildProjectDirectory)\paket.references + + false + true + true + references-file-or-cache-not-found + + + + + $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)')) + $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)')) + references-file + false + + + + + false + + + + + true + target-framework '$(TargetFramework)' or '$(TargetFrameworks)' files @(PaketResolvedFilePaths) + + + + + + + + + + false + true + + + + + + + + + + + $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) + $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) + $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) + $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5]) + + + %(PaketReferencesFileLinesInfo.PackageVersion) + All + runtime + true + + + + + $(MSBuildProjectDirectory)/obj/$(MSBuildProjectFile).paket.clitools + + + + + + + + + $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0]) + $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1]) + + + %(PaketCliToolFileLinesInfo.PackageVersion) + + + + + + + + + + false + + + + + + <_NuspecFilesNewLocation Include="$(BaseIntermediateOutputPath)$(Configuration)\*.nuspec"/> + + + + $(MSBuildProjectDirectory)/$(MSBuildProjectFile) + true + false + true + $(BaseIntermediateOutputPath)$(Configuration) + $(BaseIntermediateOutputPath) + + + + <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.nuspec"/> + + + + + + + + + + + + + + + + diff --git a/.paket/paket.bootstrapper.exe b/.paket/paket.bootstrapper.exe new file mode 100644 index 00000000..b98e000b Binary files /dev/null and b/.paket/paket.bootstrapper.exe differ diff --git a/.travis.yml b/.travis.yml index db24c598..4f77655f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,20 @@ -sudo: false language: csharp - -install: - - nuget restore ./ScriptCs.sln +dotnet: 2.0.0 +sudo: required +dist: trusty +env: + global: + - secure: m2PtYwYOhaK0uFMZ19ZxApZwWZeAIq1dS//jx/5I3txpIWD+TfycQMAWYxycFJ/GJkeVF29P4Zz1uyS2XKKjPJpp2Pds98FNQyDv3OftpLAVa0drsjfhurVlBmSdrV7GH6ncKfvhd+h7KVK5vbZc+NeR4dH7eNvN/jraS//AMJg= + - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true + - DOTNET_CLI_TELEMETRY_OPTOUT=true +mono: + - 5.8.0 + +os: + - linux + - osx script: - - mkdir artifacts --parents - - xbuild ./ScriptCs.sln /property:Configuration=Release /nologo /verbosity:normal - - mono ./packages/xunit.runners.1.9.2/tools/xunit.console.clr4.exe test/ScriptCs.Tests.Acceptance/bin/Release/ScriptCs.Tests.Acceptance.dll /xml artifacts/ScriptCs.Tests.Acceptance.dll.TestResult.xml /html artifacts/ScriptCs.Tests.Acceptance.dll.TestResult.html + - ulimit -n8192 + - chmod +x build.sh + - ./build.sh \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..fcb2be8d --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + latest + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..eb7be3ec --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index e59e1189..7261cf31 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2013, 2014 Glenn Block, Justin Rusbatch, Filip Wojcieszyn +Copyright 2013 Glenn Block, Justin Rusbatch, Filip Wojcieszyn Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index bcaffba7..01c0df77 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,13 @@ If the above fails with the error indicating that proxy authentication is requir @powershell -NoProfile -ExecutionPolicy Unrestricted -Command "[Net.WebRequest]::DefaultWebProxy.Credentials = [Net.CredentialCache]::DefaultCredentials; iex ((New-Object Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin +**Note:** If you are using a version of Chocolatey > 0.9.9.0 you can pass the `-y` into the install and upgrade commands to prevent the confirmation that will appear. + ### Installing scriptcs Once Chocolatey has been installed, you can install the latest stable version of scriptcs from your command prompt: - cinst scriptcs + choco install scriptcs Chocolatey will install scriptcs to `%LOCALAPPDATA%\scriptcs\` and update your PATH accordingly. @@ -44,35 +46,31 @@ Chocolatey will install scriptcs to `%LOCALAPPDATA%\scriptcs\` and update your P With Chocolatey, keeping scriptcs updated is just as easy: - cup scriptcs + choco upgrade scriptcs + +**Note:** If you are using a version of Chocolatey < 0.9.0.0 you will need to use `choco update scriptcs`, but also think about updating Chocolatey itself. ### Nightly builds Nightly builds are hosted on [MyGet](https://www.myget.org/), and can also be installed through with Chocolatey: - cinst scriptcs -pre -source https://www.myget.org/F/scriptcsnightly/ + choco install scriptcs -pre -source https://www.myget.org/F/scriptcsnightly/ ### Building from source #### Windows -1. Ensure you have .NET Framework 4.5 installed. +1. Ensure you have .NET Framework 4.6.1 installed. -1. Execute the build script. +2. Execute the build script. `build.cmd` #### Linux -1. Ensure you have Mono development tools 3.0 or later installed. - - `sudo apt-get install mono-devel` - -1. Ensure your mono instance has root SSL certificates - - `mozroots --import --sync` +1. Ensure you have [Mono 5.12](https://www.mono-project.com/download/stable/) or later installed. -1. Execute the build script +2. Execute the build script `./build.sh` @@ -120,22 +118,12 @@ using Raven.Client.Indexes; Console.WriteLine("Starting RavenDB server..."); -EmbeddableDocumentStore documentStore = null; -try +using (var documentStore = new EmbeddableDocumentStore { UseEmbeddedHttpServer = true }) { - documentStore = new EmbeddableDocumentStore { UseEmbeddedHttpServer = true }; documentStore.Initialize(); - - var url = string.Format("http://localhost:{0}", documentStore.Configuration.Port); - Console.WriteLine("RavenDB started, listening on {0}.", url); - + Console.WriteLine($"RavenDB started, listening on http://localhost:{documentStore.Configuration.Port}"); Console.ReadKey(); } -finally -{ - if (documentStore != null) - documentStore.Dispose(); -} ``` * Install the [RavenDB.Embedded](https://nuget.org/packages/RavenDB.Embedded/) package from NuGet using the [install command](https://github.com/scriptcs/scriptcs/wiki/Package-installation). @@ -161,7 +149,7 @@ RavenDB started, listening on http://localhost:8080. Script Packs can be used to further reduce the amount of code you need to write when working with common frameworks. -* In an empty directory, install the [ScriptCs.WebApi](https://nuget.org/packages/ScriptCs.WebApi/) script pack from NuGet. The script pack will automatically imports the Web API namespaces and provides a convenient factory method for initializing the Web API host. It also replaces the default `ControllerResolver` with a custom implementation that allows Web API to discover controllers declared in scripts. +* In an empty directory, install the [ScriptCs.WebApi](https://nuget.org/packages/ScriptCs.WebApi/) script pack from NuGet. The script pack automatically imports the Web API namespaces and provides a convenient factory method for initializing the Web API host. It also replaces the default `ControllerResolver` with a custom implementation that allows Web API to discover controllers declared in scripts. ```batchfile scriptcs -install ScriptCs.WebApi @@ -241,11 +229,11 @@ Instructions for debugging scripts using Visual Studio can be found on the [wiki ### Package installation -You can install any NuGet packages directly from the scriptcs CLI. This will pull the relevant packages from NuGet, and install them in the packages folder. +You can install any NuGet packages directly from the scriptcs CLI. This will pull the relevant packages from NuGet, and install them in the scriptcs_packages folder. Once the packages are installed, you can simply start using them in your script code directly (just import the namespaces - no additional bootstrapping or DLL referencing is needed). -The `install` command will also create a `packages.config` file if you don't have one - so that you can easily redistribute your script (without having to copy the package binaries). +The `install` command will also create a `scriptcs_packages.config` file if you don't have one - so that you can easily redistribute your script (without having to copy the package binaries). - `scriptcs -install {package name}` will install the desired package from NuGet. @@ -253,9 +241,9 @@ The `install` command will also create a `packages.config` file if you don't hav scriptcs -install ServiceStack - - `scriptcs -install` (without package name) will look for the `packages.config` file located in the current execution directory, and install all the packages specified there. You only need to specify **top level** packages. + - `scriptcs -install` (without package name) will look for the `scriptcs_packages.config` file located in the current execution directory, and install all the packages specified there. You only need to specify **top level** packages. -For example, you might create the following `packages.config`: +For example, you might create the following `scriptcs_packages.config`: @@ -268,7 +256,7 @@ And then just call: scriptcs -install -As a result, all packages specified in the `packages.config`, including all dependencies, will be downloaded and installed in the `packages` folder. +As a result, all packages specified in the `scriptcs_packages.config`, including all dependencies, will be downloaded and installed in the `scriptcs_packages` folder. ## Contributing @@ -298,7 +286,7 @@ Want to chat? In addition to Twitter, you can find us on [Google Groups](https:/ * [Damian Schenkelman](http://github.com/dschenkelman) ([@dschenkelman](https://twitter.com/intent/user?screen_name=dschenkelman)) * [Kristian Hellang](http://github.com/khellang) ([@khellang](https://twitter.com/intent/user?screen_name=khellang)) * [Adam Ralph](http://github.com/adamralph) ([@adamralph](https://twitter.com/intent/user?screen_name=adamralph)) -* [Paul Bouwer](http://github.com/paulbouwer) [@pbouwer](https://twitter.com/intent/user?screen_name=pbouwer)) +* [Paul Bouwer](http://github.com/paulbouwer) ([@pbouwer](https://twitter.com/intent/user?screen_name=pbouwer)) ## Credits diff --git a/ScriptCs.Test.ruleset b/ScriptCs.Test.ruleset deleted file mode 100644 index c99131d0..00000000 --- a/ScriptCs.Test.ruleset +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ScriptCs.ruleset b/ScriptCs.ruleset deleted file mode 100644 index 0abac465..00000000 --- a/ScriptCs.ruleset +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/ScriptCs.sln b/ScriptCs.sln index ef4d90bc..756ee82f 100644 --- a/ScriptCs.sln +++ b/ScriptCs.sln @@ -1,49 +1,39 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs", "src\ScriptCs\ScriptCs.csproj", "{25080671-1A80-4041-B9C7-260578FF4849}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs", "src\ScriptCs\ScriptCs.csproj", "{25080671-1A80-4041-B9C7-260578FF4849}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Contracts", "src\ScriptCs.Contracts\ScriptCs.Contracts.csproj", "{6049E205-8B5F-4080-B023-70600E51FD64}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Contracts", "src\ScriptCs.Contracts\ScriptCs.Contracts.csproj", "{6049E205-8B5F-4080-B023-70600E51FD64}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Tests", "test\ScriptCs.Tests\ScriptCs.Tests.csproj", "{4D6A2A55-BB17-40CB-9567-EAFF80BEFDCE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Tests", "test\ScriptCs.Tests\ScriptCs.Tests.csproj", "{4D6A2A55-BB17-40CB-9567-EAFF80BEFDCE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A59C6538-62EA-4BF6-AA00-E0E9A2892D47}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Core", "src\ScriptCs.Core\ScriptCs.Core.csproj", "{E590E710-E159-48E6-A3E6-1A83D3FE732C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Core", "src\ScriptCs.Core\ScriptCs.Core.csproj", "{E590E710-E159-48E6-A3E6-1A83D3FE732C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Core.Tests", "test\ScriptCs.Core.Tests\ScriptCs.Core.Tests.csproj", "{AC228213-7356-4F0D-BA48-EBA5FB8A7506}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Core.Tests", "test\ScriptCs.Core.Tests\ScriptCs.Core.Tests.csproj", "{AC228213-7356-4F0D-BA48-EBA5FB8A7506}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{9659F354-CF48-4FD6-8E0C-E9EB3C2BDA32}" - ProjectSection(SolutionItems) = preProject - build\Build.proj = build\Build.proj - build\ScriptCs.Common.props = build\ScriptCs.Common.props - ScriptCs.ruleset = ScriptCs.ruleset - build\ScriptCs.Tasks.targets = build\ScriptCs.Tasks.targets - build\ScriptCs.Version.props = build\ScriptCs.Version.props - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Engine.Roslyn", "src\ScriptCs.Engine.Roslyn\ScriptCs.Engine.Roslyn.csproj", "{E79EC231-E27D-4057-91C9-2D001A3A8C3B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Engine.Roslyn.Tests", "test\ScriptCs.Engine.Roslyn.Tests\ScriptCs.Engine.Roslyn.Tests.csproj", "{28D11DE5-9F98-4E0A-8CCC-9CDC19110451}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Engine.Roslyn.Tests", "test\ScriptCs.Engine.Roslyn.Tests\ScriptCs.Engine.Roslyn.Tests.csproj", "{28D11DE5-9F98-4E0A-8CCC-9CDC19110451}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Hosting", "src\ScriptCs.Hosting\ScriptCs.Hosting.csproj", "{9AEF2D95-87FB-4829-B384-34BFE076D531}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Hosting", "src\ScriptCs.Hosting\ScriptCs.Hosting.csproj", "{9AEF2D95-87FB-4829-B384-34BFE076D531}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Hosting.Tests", "test\ScriptCs.Hosting.Tests\ScriptCs.Hosting.Tests.csproj", "{EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Hosting.Tests", "test\ScriptCs.Hosting.Tests\ScriptCs.Hosting.Tests.csproj", "{EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Engine.Mono", "src\ScriptCs.Engine.Mono\ScriptCs.Engine.Mono.csproj", "{E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B138045D-DC04-4A04-A2BA-F771173BCC32}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Engine.Mono.Tests", "test\ScriptCs.Engine.Mono.Tests\ScriptCs.Engine.Mono.Tests.csproj", "{D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Engine.Roslyn", "src\ScriptCs.Engine.Roslyn\ScriptCs.Engine.Roslyn.csproj", "{E79EC231-E27D-4057-91C9-2D001A3A8C3B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCs.Tests.Acceptance", "test\ScriptCs.Tests.Acceptance\ScriptCs.Tests.Acceptance.csproj", "{10684649-2922-41F5-AB9B-20B127CBF92C}" - ProjectSection(ProjectDependencies) = postProject - {25080671-1A80-4041-B9C7-260578FF4849} = {25080671-1A80-4041-B9C7-260578FF4849} - EndProjectSection +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCs.Tests.Acceptance", "test\ScriptCs.Tests.Acceptance\ScriptCs.Tests.Acceptance.csproj", "{10684649-2922-41F5-AB9B-20B127CBF92C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B138045D-DC04-4A04-A2BA-F771173BCC32}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C5ABCEF-82F9-4850-B2B8-1D7B73D9C5C3}" ProjectSection(SolutionItems) = preProject - .nuget\packages.config = .nuget\packages.config + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets EndProjectSection EndProject Global @@ -72,10 +62,6 @@ Global {AC228213-7356-4F0D-BA48-EBA5FB8A7506}.Debug|Any CPU.Build.0 = Debug|Any CPU {AC228213-7356-4F0D-BA48-EBA5FB8A7506}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC228213-7356-4F0D-BA48-EBA5FB8A7506}.Release|Any CPU.Build.0 = Release|Any CPU - {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Release|Any CPU.Build.0 = Release|Any CPU {28D11DE5-9F98-4E0A-8CCC-9CDC19110451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28D11DE5-9F98-4E0A-8CCC-9CDC19110451}.Debug|Any CPU.Build.0 = Debug|Any CPU {28D11DE5-9F98-4E0A-8CCC-9CDC19110451}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -88,14 +74,10 @@ Global {EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A}.Release|Any CPU.Build.0 = Release|Any CPU - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B}.Release|Any CPU.Build.0 = Release|Any CPU - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092}.Release|Any CPU.Build.0 = Release|Any CPU + {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E79EC231-E27D-4057-91C9-2D001A3A8C3B}.Release|Any CPU.Build.0 = Release|Any CPU {10684649-2922-41F5-AB9B-20B127CBF92C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10684649-2922-41F5-AB9B-20B127CBF92C}.Debug|Any CPU.Build.0 = Debug|Any CPU {10684649-2922-41F5-AB9B-20B127CBF92C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -109,7 +91,9 @@ Global {AC228213-7356-4F0D-BA48-EBA5FB8A7506} = {A59C6538-62EA-4BF6-AA00-E0E9A2892D47} {28D11DE5-9F98-4E0A-8CCC-9CDC19110451} = {A59C6538-62EA-4BF6-AA00-E0E9A2892D47} {EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A} = {A59C6538-62EA-4BF6-AA00-E0E9A2892D47} - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092} = {A59C6538-62EA-4BF6-AA00-E0E9A2892D47} {10684649-2922-41F5-AB9B-20B127CBF92C} = {A59C6538-62EA-4BF6-AA00-E0E9A2892D47} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AD80B19D-7299-4CBD-B59F-6DB868FF6A9C} + EndGlobalSection EndGlobal diff --git a/Settings.StyleCop b/Settings.StyleCop deleted file mode 100644 index 9416cb43..00000000 --- a/Settings.StyleCop +++ /dev/null @@ -1,283 +0,0 @@ - - - False - False - - nullable - - - - - - - TemporaryGeneratedFile_.*\.cs$|LibLog.cs - - - - - - - - - - False - - - - - False - - - - - True - - - - - True - - - - - False - - - - - True - True - - - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - - - - - - False - - - - - False - - - - - - - - - - False - - - - - False - - - - - False - - - - - - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - True - - - - - False - - - - - False - - - - - - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - False - - - - - - - \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..55cad9e8 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +version: 1.0.{build} + +image: Visual Studio 2017 + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + +init: +- git config --global core.autocrlf false + +build_script: +- cmd: + build.cmd + +test: off \ No newline at end of file diff --git a/build.cmd b/build.cmd index e290c2c2..752c9f63 100644 --- a/build.cmd +++ b/build.cmd @@ -1,25 +1,14 @@ -@echo Off -setlocal +@echo off +cls -if exist artifacts goto Build -mkdir artifacts +.paket\paket.bootstrapper.exe +if errorlevel 1 ( + exit /b %errorlevel% +) -:Build -%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild Build\Build.proj /nologo /m /v:M %* /fl /flp:LogFile=artifacts\msbuild.log;Verbosity=Diagnostic;DetailedSummary /nr:false +.paket\paket.exe restore +if errorlevel 1 ( + exit /b %errorlevel% +) -if %ERRORLEVEL% neq 0 goto BuildFail -goto BuildSuccess - -:BuildFail -echo. -echo *** BUILD FAILED *** -goto End - -:BuildSuccess -echo. -echo **** BUILD SUCCESSFUL *** -goto end - -:End -echo. -exit /b %ERRORLEVEL% \ No newline at end of file +packages\FAKE\tools\FAKE.exe build.fsx %* \ No newline at end of file diff --git a/build.fsx b/build.fsx new file mode 100644 index 00000000..51ba6692 --- /dev/null +++ b/build.fsx @@ -0,0 +1,70 @@ +#r "packages/FAKE/tools/FakeLib.dll" + +open Fake +open Fake.DotNetCli +open Fake.Testing +open System.IO + +Target "Clean" (fun _ -> + !! "artifacts" ++ "src/*/bin" ++ "test/*/bin" ++ "src/*/obj" ++ "test/*/obj" + |> DeleteDirs +) + +Target "Build" (fun _ -> + DotNetCli.Restore id + + "ScriptCs.sln" + |> MSBuildHelper.build (fun p -> + { p with + RestorePackagesFlag = true + Verbosity = Some Minimal + Targets = [ "Build" ] + Properties = + [ + "Optimize", "True" + "Configuration", "Release" + ] + } ) +) + +Target "Test" (fun _ -> +#if MONO + !! "test/**/bin/**/*Tests.Acceptance.dll" + |> xUnit2 (fun c -> + {c with + MaxThreads = CollectionConcurrencyMode.MaxThreads 1 + }) +#else + !! "test/**/*Tests*.csproj" + |> Seq.iter (fun p -> + DotNetCli.Test (fun c -> + {c with + WorkingDir = Path.GetDirectoryName p + AdditionalArgs = ["--no-build"] + }) + ) +#endif +) + +Target "Pack" (fun _ -> + "ScriptCs.sln" + |> MSBuildHelper.build (fun p -> + { p with + RestorePackagesFlag = true + Verbosity = Some Minimal + Targets = [ "Pack" ] + Properties = + [ + "Optimize", "True" + "Configuration", "Release" + "PackageOutputPath", "../../artifacts" + ] + } ) +) + +"Clean" + ==> "Build" + ==> "Test" + ==> "Pack" + +RunTargetOrDefault "Pack" \ No newline at end of file diff --git a/build.sh b/build.sh index 41b4f391..fa37cd79 100755 --- a/build.sh +++ b/build.sh @@ -1,15 +1,33 @@ -#!/usr/bin/env bash -set -e -set -o pipefail -set -x +#!/bin/bash +if test "$OS" = "Windows_NT" +then + # use .Net -# install -mozroots --import --sync --quiet -mono ./.nuget/NuGet.exe restore ./ScriptCs.sln + .paket/paket.bootstrapper.exe prerelease + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi -# script -mkdir -p artifacts/Release/bin -xbuild ./ScriptCs.sln /property:Configuration=Release /nologo /verbosity:normal -cp src/*/bin/Release/* artifacts/Release/bin/ -mono ./packages/xunit.runners.1.9.2/tools/xunit.console.clr4.exe test/ScriptCs.Tests.Acceptance/bin/Release/ScriptCs.Tests.Acceptance.dll /xml artifacts/ScriptCs.Tests.Acceptance.dll.TestResult.xml /html artifacts/ScriptCs.Tests.Acceptance.dll.TestResult.html + .paket/paket.exe restore + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi + packages/FAKE/tools/FAKE.exe $@ --nocache --fsiargs build.fsx +else + # use mono + mono .paket/paket.bootstrapper.exe prerelease + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi + + mono .paket/paket.exe restore + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi + mono packages/FAKE/tools/FAKE.exe $@ --nocache --fsiargs -d:MONO build.fsx +fi \ No newline at end of file diff --git a/build/Build.proj b/build/Build.proj deleted file mode 100644 index 7138da9f..00000000 --- a/build/Build.proj +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - Release - - $(MSBuildThisFileDirectory)..\ - $(Root)artifacts\ - $(BaseArtifactsPath)$(Configuration)\ - $(BaseArtifactsPath)$(Configuration) - - $(Root).nuget\NuGet.exe - - $([System.IO.Path]::Combine( $(Root), 'common\CommonVersionInfo.cs' )) - - - - - - Configuration=$(Configuration); - ArtifactsPath=$(ArtifactsPath); - RunCodeAnalysis=false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Version $(PackageVersion) -Symbols -Verbosity quiet -NoPackageAnalysis -OutputDirectory "$(PackageOutputPath)" -p Configuration=$(Configuration) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/ScriptCs.Common.props b/build/ScriptCs.Common.props deleted file mode 100644 index 0a1bcee9..00000000 --- a/build/ScriptCs.Common.props +++ /dev/null @@ -1,32 +0,0 @@ - - - - bin\$(Configuration)\ - true - prompt - 4 - true - - true - - - - $(DefineConstants);MONO - false - - - - false - - - - $(DefineConstants);DEBUG - full - false - - - - pdbonly - true - - \ No newline at end of file diff --git a/build/ScriptCs.Tasks.targets b/build/ScriptCs.Tasks.targets deleted file mode 100644 index 3c48de20..00000000 --- a/build/ScriptCs.Tasks.targets +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/ScriptCs.Version.props b/build/ScriptCs.Version.props deleted file mode 100644 index 3e888860..00000000 --- a/build/ScriptCs.Version.props +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - 0 - 15 - 0 - - - - - - - - - $(MajorVersion).$(MinorVersion).0 - $(MajorVersion).$(MinorVersion).$(PatchVersion) - $(AssemblyInformationalVersion)-$(BuildQuality) - $(AssemblyInformationalVersion)-nightly-$([System.DateTime]::UtcNow.ToString("yyMMdd")) - - $(AssemblyInformationalVersion) - - - - - <_Parameter1>$(AssemblyVersion) - - - <_Parameter1>$(AssemblyInformationalVersion) - - - diff --git a/build_brew.sh b/build_brew.sh new file mode 100755 index 00000000..b74b80e4 --- /dev/null +++ b/build_brew.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e +set -o pipefail +set -x + +# install +mozroots --import --sync --quiet +mono ./.nuget/NuGet.exe restore ./ScriptCs.sln + +# script +mkdir -p artifacts/Release/bin +msbuild ./ScriptCs.sln /property:Configuration=Release /nologo /verbosity:normal +cp src/ScriptCs/bin/Release/net461/* artifacts/Release/bin/ diff --git a/common/CommonAssemblyInfo.cs b/common/CommonAssemblyInfo.cs deleted file mode 100644 index 1bf26ab7..00000000 --- a/common/CommonAssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -#if DEBUG -[assembly: AssemblyConfiguration("Debug")] -#else -[assembly: AssemblyConfiguration("Release")] -#endif - -[assembly: AssemblyProduct("scriptcs")] -[assembly: AssemblyCompany("Glenn Block, Filip Wojcieszyn, Justin Rusbatch, Kristian Hellang, Damian Schenkelman, Adam Ralph")] -[assembly: AssemblyCopyright("Copyright 2013 Glenn Block, Justin Rusbatch, Filip Wojcieszyn")] - -[assembly: ComVisible(false)] -[assembly: CLSCompliant(false)] \ No newline at end of file diff --git a/common/CommonVersionInfo.cs b/common/CommonVersionInfo.cs deleted file mode 100644 index 590a13eb..00000000 --- a/common/CommonVersionInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Reflection; - -/** - * Do not manually edit this file. The build script will generate and insert the proper version numbers based on the - * contents of 'build\ScriptCs.Version.props'. - **/ - -[assembly: AssemblyVersion("0.0.0")] -[assembly: AssemblyInformationalVersion("0.0.0")] \ No newline at end of file diff --git a/common/Icon.ico b/common/Icon.ico deleted file mode 100644 index d946fa05..00000000 Binary files a/common/Icon.ico and /dev/null differ diff --git a/edit-stylecop-settings.cmd b/edit-stylecop-settings.cmd deleted file mode 100644 index c45a2fe2..00000000 --- a/edit-stylecop-settings.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@echo Off -start packages\StyleCop.MSBuild.4.7.49.0\tools\StyleCopSettingsEditor.exe Settings.StyleCop \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies new file mode 100644 index 00000000..a397754e --- /dev/null +++ b/paket.dependencies @@ -0,0 +1,4 @@ +source https://nuget.org/api/v2 + +nuget FAKE +nuget xunit.runner.console \ No newline at end of file diff --git a/paket.lock b/paket.lock new file mode 100644 index 00000000..a878f015 --- /dev/null +++ b/paket.lock @@ -0,0 +1,4 @@ +NUGET + remote: https://www.nuget.org/api/v2 + FAKE (4.64.13) + xunit.runner.console (2.4.1) diff --git a/src/ScriptCs.Contracts/AssemblyReferences.cs b/src/ScriptCs.Contracts/AssemblyReferences.cs index e33d2cce..734bcea7 100644 --- a/src/ScriptCs.Contracts/AssemblyReferences.cs +++ b/src/ScriptCs.Contracts/AssemblyReferences.cs @@ -61,15 +61,9 @@ public AssemblyReferences(IEnumerable assemblies, IEnumerable } } - public IEnumerable Assemblies - { - get { return _assemblies.Values.ToArray(); } - } + public IEnumerable Assemblies => _assemblies.Values.ToArray(); - public IEnumerable Paths - { - get { return _paths.Values.ToArray(); } - } + public IEnumerable Paths => _paths.Values.ToArray(); public AssemblyReferences Union(AssemblyReferences references) { diff --git a/src/ScriptCs.Contracts/DefaultLogProvider.cs b/src/ScriptCs.Contracts/DefaultLogProvider.cs new file mode 100644 index 00000000..202dae56 --- /dev/null +++ b/src/ScriptCs.Contracts/DefaultLogProvider.cs @@ -0,0 +1,76 @@ +namespace ScriptCs.Contracts +{ + using System; + + public class DefaultLogProvider : ILogProvider + { + private readonly ILogProvider _provider = ResolveLogProvider() ?? new NullLogProvider(); + + [Obsolete("Should not be called directly. Instead, call a method on LogProviderExtensions.")] + public Logger GetLogger(string name) => _provider.GetLogger(name); + + /// + /// Opens a nested diagnostics context. Not supported in . + /// + /// The message to add to the diagnostics context. + /// A disposable that when disposed removes the message from the context. + public IDisposable OpenNestedContext(string message) => _provider.OpenNestedContext(message); + + /// + /// Opens a mapped diagnostics context. Not supported in . + /// + /// A key. + /// A value. + /// A disposable that when disposed removes the map from the context. + public IDisposable OpenMappedContext(string key, string value) => _provider.OpenMappedContext(key, value); + + private static ILogProvider ResolveLogProvider() + { + var libLogProvider = LibLog.LogProvider.ResolveLogProvider(); + return libLogProvider == null ? null : new LibLogAdapter(libLogProvider); + } + + private class NullLogProvider : ILogProvider + { + private static readonly Logger logger = (_, __, ___, ____) => false; + private static readonly Disposable disposable = new Disposable(); + + [Obsolete("Should not be called directly. Instead, call a method on LogProviderExtensions.")] + public Logger GetLogger(string name) + { + return logger; + } + + public IDisposable OpenNestedContext(string message) => disposable; + + public IDisposable OpenMappedContext(string key, string value) => disposable; + + private sealed class Disposable : IDisposable + { + public void Dispose() + { + } + } + } + + private class LibLogAdapter : ILogProvider + { + private readonly LibLog.ILogProvider _libLogProvider; + + public LibLogAdapter(LibLog.ILogProvider libLogProvider) + { + _libLogProvider = libLogProvider; + } + + public Logger GetLogger(string name) + { + return (logLevel, messageFunc, exception, formatParameters) => + _libLogProvider.GetLogger(name)((LibLog.LogLevel)logLevel, messageFunc, exception, formatParameters); + } + + public IDisposable OpenNestedContext(string message) => _libLogProvider.OpenNestedContext(message); + + public IDisposable OpenMappedContext(string key, string value) => _libLogProvider.OpenMappedContext(key, value); + } + } +} diff --git a/src/ScriptCs.Contracts/DirectiveLineProcessor.cs b/src/ScriptCs.Contracts/DirectiveLineProcessor.cs index d05be903..9494a54b 100644 --- a/src/ScriptCs.Contracts/DirectiveLineProcessor.cs +++ b/src/ScriptCs.Contracts/DirectiveLineProcessor.cs @@ -4,17 +4,11 @@ namespace ScriptCs.Contracts { public abstract class DirectiveLineProcessor : IDirectiveLineProcessor { - protected virtual BehaviorAfterCode BehaviorAfterCode - { - get { return BehaviorAfterCode.Ignore; } - } + protected virtual BehaviorAfterCode BehaviorAfterCode => BehaviorAfterCode.Ignore; protected abstract string DirectiveName { get; } - private string DirectiveString - { - get { return string.Format("#{0}", DirectiveName); } - } + private string DirectiveString => string.Format("#{0}", DirectiveName); public bool ProcessLine(IFileParser parser, FileParserContext context, string line, bool isBeforeCode) { @@ -29,7 +23,7 @@ public bool ProcessLine(IFileParser parser, FileParserContext context, string li { throw new InvalidDirectiveUseException(string.Format("Encountered directive '{0}' after the start of code. Please move this directive to the beginning of the file.", DirectiveString)); } - else if (BehaviorAfterCode == Contracts.BehaviorAfterCode.Ignore) + if (BehaviorAfterCode == Contracts.BehaviorAfterCode.Ignore) { return true; } diff --git a/src/ScriptCs.Contracts/FileParserContext.cs b/src/ScriptCs.Contracts/FileParserContext.cs index 312a6f5f..adc754d8 100644 --- a/src/ScriptCs.Contracts/FileParserContext.cs +++ b/src/ScriptCs.Contracts/FileParserContext.cs @@ -19,5 +19,7 @@ public FileParserContext() public List LoadedScripts { get; private set; } public List BodyLines { get; private set; } + + public string ScriptPath { get; set; } } } \ No newline at end of file diff --git a/src/ScriptCs.Contracts/FilePreProcessorResult.cs b/src/ScriptCs.Contracts/FilePreProcessorResult.cs index 7406b3a3..2b664edc 100644 --- a/src/ScriptCs.Contracts/FilePreProcessorResult.cs +++ b/src/ScriptCs.Contracts/FilePreProcessorResult.cs @@ -17,6 +17,8 @@ public FilePreProcessorResult() public List References { get; set; } + public string ScriptPath { get; set; } + public string Code { get; set; } } } \ No newline at end of file diff --git a/src/ScriptCs.Contracts/IConsole.cs b/src/ScriptCs.Contracts/IConsole.cs index be06d0d9..2ff69cdc 100644 --- a/src/ScriptCs.Contracts/IConsole.cs +++ b/src/ScriptCs.Contracts/IConsole.cs @@ -10,7 +10,7 @@ public interface IConsole void WriteLine(string value); - string ReadLine(); + string ReadLine(string prompt); void Clear(); @@ -19,5 +19,7 @@ public interface IConsole void ResetColor(); ConsoleColor ForegroundColor { get; set; } + + int Width { get; } } } \ No newline at end of file diff --git a/src/ScriptCs.Contracts/IFileSystem.cs b/src/ScriptCs.Contracts/IFileSystem.cs index 1c6921f1..0f0fb352 100644 --- a/src/ScriptCs.Contracts/IFileSystem.cs +++ b/src/ScriptCs.Contracts/IFileSystem.cs @@ -35,6 +35,8 @@ IEnumerable EnumerateFilesAndDirectories( string GetFullPath(string path); + string TempPath { get; } + string CurrentDirectory { get; set; } string NewLine { get; } diff --git a/src/ScriptCs.Contracts/IFileSystemMigrator.cs b/src/ScriptCs.Contracts/IFileSystemMigrator.cs deleted file mode 100644 index 3a8ca5c4..00000000 --- a/src/ScriptCs.Contracts/IFileSystemMigrator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ScriptCs.Contracts -{ - public interface IFileSystemMigrator - { - void Migrate(); - } -} diff --git a/src/ScriptCs.Contracts/ILog.cs b/src/ScriptCs.Contracts/ILog.cs new file mode 100644 index 00000000..795e61b2 --- /dev/null +++ b/src/ScriptCs.Contracts/ILog.cs @@ -0,0 +1,36 @@ +namespace ScriptCs.Contracts +{ + using System; + + public interface ILog + { + /// + /// Logs a message for a specified log level, if the is enabled for that log level. + /// + /// The log level. + /// + /// Optional function which creates the message. + /// Specify null to simply check if the logger is enabled for the . + /// + /// An optional exception to include with the message. + /// + /// Optional arguments for formatting the message created by . + /// + /// + /// true if the is enabled for the . + /// Otherwise false. + /// + /// + /// Note to implementers for optimal performance: + /// When the is null, simply return a + /// indicating whether the is enabled for the specified . + /// When the is not null, it should only be invoked + /// if the is enabled for the specified . + /// + bool Log( + LogLevel logLevel, + Func createMessage = null, + Exception exception = null, + params object[] formatArgs); + } +} diff --git a/src/ScriptCs.Contracts/ILogProvider.cs b/src/ScriptCs.Contracts/ILogProvider.cs new file mode 100644 index 00000000..17946070 --- /dev/null +++ b/src/ScriptCs.Contracts/ILogProvider.cs @@ -0,0 +1,21 @@ +namespace ScriptCs.Contracts +{ + using System; + + public interface ILogProvider + { + /// + /// Gets the specified named logger. + /// + /// Name of the logger. + /// The logger reference. + /// + /// Do not call this method directly. Instead, call a method on . + /// + Logger GetLogger(string name); + + IDisposable OpenNestedContext(string message); + + IDisposable OpenMappedContext(string key, string value); + } +} diff --git a/src/ScriptCs.Contracts/IScriptEnvironment.cs b/src/ScriptCs.Contracts/IScriptEnvironment.cs index 3ebcec7d..60a6c637 100644 --- a/src/ScriptCs.Contracts/IScriptEnvironment.cs +++ b/src/ScriptCs.Contracts/IScriptEnvironment.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -9,5 +10,12 @@ namespace ScriptCs.Contracts public interface IScriptEnvironment { IReadOnlyList ScriptArgs { get; } + void AddCustomPrinter(Func printer); + void Print(T o); + void Print(object o); + string ScriptPath { get; } + string[] LoadedScripts { get; } + Assembly ScriptAssembly { get; } + void Initialize(); } -} +} \ No newline at end of file diff --git a/src/ScriptCs.Contracts/IScriptExecutor.cs b/src/ScriptCs.Contracts/IScriptExecutor.cs index 0dba0d22..fe9b992d 100644 --- a/src/ScriptCs.Contracts/IScriptExecutor.cs +++ b/src/ScriptCs.Contracts/IScriptExecutor.cs @@ -7,7 +7,7 @@ public interface IScriptExecutor { AssemblyReferences References { get; } - ICollection Namespaces { get; } + IReadOnlyCollection Namespaces { get; } IScriptEngine ScriptEngine { get; } diff --git a/src/ScriptCs.Contracts/IScriptInfo.cs b/src/ScriptCs.Contracts/IScriptInfo.cs new file mode 100644 index 00000000..1686a1af --- /dev/null +++ b/src/ScriptCs.Contracts/IScriptInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptCs.Contracts +{ + public interface IScriptInfo + { + string ScriptPath { get; set; } + IList LoadedScripts { get; } + } +} diff --git a/src/ScriptCs.Core/App_Packages/LibLog.3.1/LibLog.cs b/src/ScriptCs.Contracts/Internal/LibLog.cs similarity index 71% rename from src/ScriptCs.Core/App_Packages/LibLog.3.1/LibLog.cs rename to src/ScriptCs.Contracts/Internal/LibLog.cs index cc0c7d40..f5f232ac 100644 --- a/src/ScriptCs.Core/App_Packages/LibLog.3.1/LibLog.cs +++ b/src/ScriptCs.Contracts/Internal/LibLog.cs @@ -3,7 +3,7 @@ // // https://github.com/damianh/LibLog //=============================================================================== -// Copyright © 2011-2015 Damian Hickey. All rights reserved. +// Copyright © 2011-2015 Damian Hickey. 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 @@ -27,20 +27,62 @@ // ReSharper disable PossibleNullReferenceException // Define LIBLOG_PORTABLE conditional compilation symbol for PCL compatibility +// +// Define LIBLOG_PUBLIC to enable ability to GET a logger (LogProvider.For<>() etc) from outside this library. NOTE: +// this can have unintended consequences of consumers of your library using your library to resolve a logger. If the +// reason is because you want to open this functionality to other projects within your solution, +// consider [InternalsVisibleTo] instead. +// +// Define LIBLOG_PROVIDERS_ONLY if your library provides its own logging API and you just want to use the +// LibLog providers internally to provide built in support for popular logging frameworks. #pragma warning disable 1591 -namespace ScriptCs.Logging +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "ScriptCs.Contracts.Logging")] +[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "ScriptCs.Contracts.Logging.Logger.#Invoke(ScriptCs.Contracts.Logging.LogLevel,System.Func`1,System.Exception,System.Object[])")] + +// If you copied this file manually, you need to change all "YourRootNameSpace" so not to clash with other libraries +// that use LibLog +#if LIBLOG_PROVIDERS_ONLY +namespace ScriptCs.Contracts.LibLog +#else +namespace ScriptCs.Contracts.Logging +#endif { using System.Collections.Generic; - using ScriptCs.Logging.LogProviders; + using System.Diagnostics.CodeAnalysis; +#if LIBLOG_PROVIDERS_ONLY + using ScriptCs.Contracts.LibLog.LogProviders; +#else + using ScriptCs.Contracts.Logging.LogProviders; +#endif using System; +#if !LIBLOG_PROVIDERS_ONLY using System.Diagnostics; +#if !LIBLOG_PORTABLE + using System.Runtime.CompilerServices; +#endif +#endif +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + delegate bool Logger(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); + +#if !LIBLOG_PROVIDERS_ONLY /// /// Simple interface that represent a logger. /// - public interface ILog +#if LIBLOG_PUBLIC + public +#else + internal +#endif + interface ILog { /// /// Log a message the specified log level. @@ -58,11 +100,17 @@ public interface ILog /// bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters ); } +#endif /// /// The log level. /// - public enum LogLevel +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + enum LogLevel { Trace, Debug, @@ -72,7 +120,13 @@ public enum LogLevel Fatal } - public static class LogExtensions +#if !LIBLOG_PROVIDERS_ONLY +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static partial class LogExtensions { public static bool IsDebugEnabled(this ILog logger) { @@ -140,7 +194,6 @@ public static void DebugException(this ILog logger, string message, Exception ex } } - public static void DebugException(this ILog logger, string message, Exception exception, params object[] formatParams) { if (logger.IsDebugEnabled()) @@ -151,6 +204,7 @@ public static void DebugException(this ILog logger, string message, Exception ex public static void Error(this ILog logger, Func messageFunc) { + GuardAgainstNullLogger(logger); logger.Log(LogLevel.Error, messageFunc); } @@ -297,6 +351,7 @@ public static void WarnException(this ILog logger, string message, Exception exc } } + // ReSharper disable once UnusedParameter.Local private static void GuardAgainstNullLogger(ILog logger) { if (logger == null) @@ -321,18 +376,24 @@ private static T Return(this T value) return value; } } +#endif /// /// Represents a way to get a /// - public interface ILogProvider +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + interface ILogProvider { /// /// Gets the specified named logger. /// /// Name of the logger. /// The logger reference. - ILog GetLogger(string name); + Logger GetLogger(string name); /// /// Opens a nested diagnostics context. Not supported in EntLib logging. @@ -353,18 +414,83 @@ public interface ILogProvider /// /// Provides a mechanism to create instances of objects. /// - public static class LogProvider +#if LIBLOG_PROVIDERS_ONLY + internal +#else + public +#endif + static class LogProvider { - private static ILogProvider _currentLogProvider; +#if !LIBLOG_PROVIDERS_ONLY + /// + /// The disable logging environment variable. If the environment variable is set to 'true', then logging + /// will be disabled. + /// + public const string DisableLoggingEnvironmentVariable = "ScriptCs.Contracts_LIBLOG_DISABLE"; private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " + "with a non-null value first."; + private static dynamic s_currentLogProvider; + private static Action s_onCurrentLogProviderSet; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static LogProvider() + { + IsDisabled = false; + } + + /// + /// Sets the current log provider. + /// + /// The log provider. + public static void SetCurrentLogProvider(ILogProvider logProvider) + { + s_currentLogProvider = logProvider; + + RaiseOnCurrentLogProviderSet(); + } + + /// + /// Gets or sets a value indicating whether this is logging is disabled. + /// + /// + /// true if logging is disabled; otherwise, false. + /// + public static bool IsDisabled { get; set; } + + /// + /// Sets an action that is invoked when a consumer of your library has called SetCurrentLogProvider. It is + /// important that hook into this if you are using child libraries (especially ilmerged ones) that are using + /// LibLog (or other logging abstraction) so you adapt and delegate to them. + /// + /// + internal static Action OnCurrentLogProviderSet + { + set + { + s_onCurrentLogProviderSet = value; + RaiseOnCurrentLogProviderSet(); + } + } + + internal static ILogProvider CurrentLogProvider + { + get + { + return s_currentLogProvider; + } + } /// /// Gets a logger for the specified type. /// /// The type whose name will be used for the logger. /// An instance of - public static ILog For() +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog For() { return GetLogger(typeof(T)); } @@ -374,7 +500,13 @@ public static ILog For() /// Gets a logger for the current class. /// /// An instance of - public static ILog GetCurrentClassLogger() + [MethodImpl(MethodImplOptions.NoInlining)] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetCurrentClassLogger() { var stackFrame = new StackFrame(1, false); return GetLogger(stackFrame.GetMethod().DeclaringType); @@ -386,7 +518,12 @@ public static ILog GetCurrentClassLogger() /// /// The type whose name will be used for the logger. /// An instance of - public static ILog GetLogger(Type type) +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetLogger(Type type) { return GetLogger(type.FullName); } @@ -396,44 +533,77 @@ public static ILog GetLogger(Type type) /// /// The name. /// An instance of - public static ILog GetLogger(string name) +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetLogger(string name) { - ILogProvider logProvider = _currentLogProvider ?? ResolveLogProvider(); - return logProvider == null ? new NoOpLogger() : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name)); + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + return logProvider == null + ? NoOpLogger.Instance + : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled); } /// - /// Sets the current log provider. + /// Opens a nested diagnostics context. /// - /// The log provider. - public static void SetCurrentLogProvider(ILogProvider logProvider) + /// A message. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenNestedContext(string message) { - _currentLogProvider = logProvider; + return CurrentLogProvider == null + ? new DisposableAction(() => {}) + : CurrentLogProvider.OpenNestedContext(message); } - public static IDisposable OpenNestedConext(string message) - { - if(_currentLogProvider == null) - { - throw new InvalidOperationException(NullLogProvider); - } - return _currentLogProvider.OpenNestedContext(message); - } - - public static IDisposable OpenMappedContext(string key, string value) + /// + /// Opens a mapped diagnostics context. + /// + /// A key. + /// A value. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenMappedContext(string key, string value) { - if (_currentLogProvider == null) - { - throw new InvalidOperationException(NullLogProvider); - } - return _currentLogProvider.OpenMappedContext(key, value); + return CurrentLogProvider == null + ? new DisposableAction(() => { }) + : CurrentLogProvider.OpenMappedContext(key, value); } +#endif - public delegate bool IsLoggerAvailable(); +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + delegate bool IsLoggerAvailable(); - public delegate ILogProvider CreateLogProvider(); +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + delegate ILogProvider CreateLogProvider(); - public static readonly List> LogProviderResolvers = +#if LIBLOG_PROVIDERS_ONLY + private +#else + internal +#endif + static readonly List> LogProviderResolvers = new List> { new Tuple(SerilogLogProvider.IsLoggerAvailable, () => new SerilogLogProvider()), @@ -441,10 +611,21 @@ public static IDisposable OpenMappedContext(string key, string value) new Tuple(Log4NetLogProvider.IsLoggerAvailable, () => new Log4NetLogProvider()), new Tuple(EntLibLogProvider.IsLoggerAvailable, () => new EntLibLogProvider()), new Tuple(LoupeLogProvider.IsLoggerAvailable, () => new LoupeLogProvider()), - new Tuple(ColouredConsoleLogProvider.IsLoggerAvailable, () => new ColouredConsoleLogProvider()), }; - private static ILogProvider ResolveLogProvider() +#if !LIBLOG_PROVIDERS_ONLY + private static void RaiseOnCurrentLogProviderSet() + { + if (s_onCurrentLogProviderSet != null) + { + s_onCurrentLogProviderSet(s_currentLogProvider); + } + } +#endif + + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal static ILogProvider ResolveLogProvider() { try { @@ -463,42 +644,63 @@ private static ILogProvider ResolveLogProvider() #else Console.WriteLine( #endif - "Exception occured resolving a log provider. Logging for this assembly {0} is disabled. {1}", + "Exception occurred resolving a log provider. Logging for this assembly {0} is disabled. {1}", typeof(LogProvider).GetAssemblyPortable().FullName, ex); } return null; } +#if !LIBLOG_PROVIDERS_ONLY internal class NoOpLogger : ILog { + internal static readonly NoOpLogger Instance = new NoOpLogger(); + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) { return false; } } +#endif } +#if !LIBLOG_PROVIDERS_ONLY internal class LoggerExecutionWrapper : ILog { - private readonly ILog _logger; + private readonly Logger _logger; + private readonly Func _getIsDisabled; internal const string FailedToGenerateLogMessage = "Failed to generate log message"; - internal LoggerExecutionWrapper(ILog logger) + internal LoggerExecutionWrapper(Logger logger, Func getIsDisabled = null) { _logger = logger; + _getIsDisabled = getIsDisabled ?? (() => false); } - public ILog WrappedLogger + internal Logger WrappedLogger { get { return _logger; } } + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) { + if (_getIsDisabled()) + { + return false; + } +#if !LIBLOG_PORTABLE + var envVar = Environment.GetEnvironmentVariable(LogProvider.DisableLoggingEnvironmentVariable); + + if (envVar != null && envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) + { + return false; + } +#endif + if (messageFunc == null) { - return _logger.Log(logLevel, null); + return _logger(logLevel, null); } Func wrappedMessageFunc = () => @@ -513,19 +715,31 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception } return null; }; - return _logger.Log(logLevel, wrappedMessageFunc, exception, formatParameters); + return _logger(logLevel, wrappedMessageFunc, exception, formatParameters); } } +#endif } -namespace ScriptCs.Logging.LogProviders +#if LIBLOG_PROVIDERS_ONLY +namespace ScriptCs.Contracts.LibLog.LogProviders +#else +namespace ScriptCs.Contracts.Logging.LogProviders +#endif { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; +#if !LIBLOG_PORTABLE + using System.Diagnostics; +#endif using System.Globalization; + using System.Linq; using System.Linq.Expressions; using System.Reflection; +#if !LIBLOG_PORTABLE using System.Text; +#endif using System.Text.RegularExpressions; internal abstract class LogProviderBase : ILogProvider @@ -545,8 +759,8 @@ protected LogProviderBase() = new Lazy(GetOpenMdcMethod); } - public abstract ILog GetLogger(string name); - + public abstract Logger GetLogger(string name); + public IDisposable OpenNestedContext(string message) { return _lazyOpenNdcMethod.Value(message); @@ -571,8 +785,10 @@ protected virtual OpenMdc GetOpenMdcMethod() internal class NLogLogProvider : LogProviderBase { private readonly Func _getLoggerByNameDelegate; - private static bool _providerIsAvailableOverride = true; + private static bool s_providerIsAvailableOverride = true; + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "NLog")] public NLogLogProvider() { if (!IsLoggerAvailable()) @@ -584,13 +800,13 @@ public NLogLogProvider() public static bool ProviderIsAvailableOverride { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } } - public override ILog GetLogger(string name) + public override Logger GetLogger(string name) { - return new NLogLogger(_getLoggerByNameDelegate(name)); + return new NLogLogger(_getLoggerByNameDelegate(name)).Log; } public static bool IsLoggerAvailable() @@ -601,7 +817,7 @@ public static bool IsLoggerAvailable() protected override OpenNdc GetOpenNdcMethod() { Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog"); - MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", new[] { typeof(string) }); + MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", typeof(string)); ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); return Expression.Lambda(pushMethodCall, messageParam).Compile(); @@ -611,8 +827,8 @@ protected override OpenMdc GetOpenMdcMethod() { Type mdcContextType = Type.GetType("NLog.MappedDiagnosticsContext, NLog"); - MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", new[] { typeof(string), typeof(string) }); - MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", new[] { typeof(string) }); + MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", typeof(string), typeof(string)); + MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", typeof(string)); ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); @@ -641,13 +857,13 @@ private static Type GetLogManagerType() private static Func GetGetLoggerMethodCall() { Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("GetLogger", new[] { typeof(string) }); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); MethodCallExpression methodCall = Expression.Call(null, method, nameParam); return Expression.Lambda>(methodCall, nameParam).Compile(); } - public class NLogLogger : ILog + internal class NLogLogger { private readonly dynamic _logger; @@ -656,6 +872,7 @@ internal NLogLogger(dynamic logger) _logger = logger; } + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) { if (messageFunc == null) @@ -716,6 +933,7 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception return false; } + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) { switch (logLevel) @@ -790,8 +1008,9 @@ private bool IsLogLevelEnable(LogLevel logLevel) internal class Log4NetLogProvider : LogProviderBase { private readonly Func _getLoggerByNameDelegate; - private static bool _providerIsAvailableOverride = true; + private static bool s_providerIsAvailableOverride = true; + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] public Log4NetLogProvider() { if (!IsLoggerAvailable()) @@ -803,13 +1022,13 @@ public Log4NetLogProvider() public static bool ProviderIsAvailableOverride { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } } - public override ILog GetLogger(string name) + public override Logger GetLogger(string name) { - return new Log4NetLogger(_getLoggerByNameDelegate(name)); + return new Log4NetLogger(_getLoggerByNameDelegate(name)).Log; } internal static bool IsLoggerAvailable() @@ -819,28 +1038,56 @@ internal static bool IsLoggerAvailable() protected override OpenNdc GetOpenNdcMethod() { - Type ndcContextType = Type.GetType("log4net.NDC, log4net"); - MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", new[] { typeof(string) }); - ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); - MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); - return Expression.Lambda(pushMethodCall, messageParam).Compile(); + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo stacksProperty = logicalThreadContextType.GetPropertyPortable("Stacks"); + Type logicalThreadContextStacksType = stacksProperty.PropertyType; + PropertyInfo stacksIndexerProperty = logicalThreadContextStacksType.GetPropertyPortable("Item"); + Type stackType = stacksIndexerProperty.PropertyType; + MethodInfo pushMethod = stackType.GetMethodPortable("Push"); + + ParameterExpression messageParameter = + Expression.Parameter(typeof(string), "message"); + + // message => LogicalThreadContext.Stacks.Item["NDC"].Push(message); + MethodCallExpression callPushBody = + Expression.Call( + Expression.Property(Expression.Property(null, stacksProperty), + stacksIndexerProperty, + Expression.Constant("NDC")), + pushMethod, + messageParameter); + + OpenNdc result = + Expression.Lambda(callPushBody, messageParameter) + .Compile(); + + return result; } protected override OpenMdc GetOpenMdcMethod() { - Type mdcContextType = Type.GetType("log4net.MDC, log4net"); + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo propertiesProperty = logicalThreadContextType.GetPropertyPortable("Properties"); + Type logicalThreadContextPropertiesType = propertiesProperty.PropertyType; + PropertyInfo propertiesIndexerProperty = logicalThreadContextPropertiesType.GetPropertyPortable("Item"); + + MethodInfo removeMethod = logicalThreadContextPropertiesType.GetMethodPortable("Remove"); - MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", new[] { typeof(string), typeof(string) }); - MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", new[] { typeof(string) }); ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); - MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam); - MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam); + MemberExpression propertiesExpression = Expression.Property(null, propertiesProperty); + + // (key, value) => LogicalThreadContext.Properties.Item[key] = value; + BinaryExpression setProperties = Expression.Assign(Expression.Property(propertiesExpression, propertiesIndexerProperty, keyParam), valueParam); + + // key => LogicalThreadContext.Properties.Remove(key); + MethodCallExpression removeMethodCall = Expression.Call(propertiesExpression, removeMethod, keyParam); Action set = Expression - .Lambda>(setMethodCall, keyParam, valueParam) + .Lambda>(setProperties, keyParam, valueParam) .Compile(); + Action remove = Expression .Lambda>(removeMethodCall, keyParam) .Compile(); @@ -860,19 +1107,82 @@ private static Type GetLogManagerType() private static Func GetGetLoggerMethodCall() { Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("GetLogger", new[] { typeof(string) }); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); MethodCallExpression methodCall = Expression.Call(null, method, nameParam); return Expression.Lambda>(methodCall, nameParam).Compile(); } - public class Log4NetLogger : ILog + internal class Log4NetLogger { private readonly dynamic _logger; - + private static Type s_callerStackBoundaryType; + private static readonly object CallerStackBoundaryTypeSync = new object(); + + private readonly object _levelDebug; + private readonly object _levelInfo; + private readonly object _levelWarn; + private readonly object _levelError; + private readonly object _levelFatal; + private readonly Func _isEnabledForDelegate; + private readonly Action _logDelegate; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] internal Log4NetLogger(dynamic logger) { - _logger = logger; + _logger = logger.Logger; + + var logEventLevelType = Type.GetType("log4net.Core.Level, log4net"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type log4net.Core.Level was not found."); + } + + var levelFields = logEventLevelType.GetFieldsPortable().ToList(); + _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); + _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); + _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); + _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); + _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); + + // Func isEnabledFor = (logger, level) => { return ((log4net.Core.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("log4net.Core.ILogger, log4net"); + if (loggerType == null) + { + throw new InvalidOperationException("Type log4net.Core.ILogger, was not found."); + } + MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabledFor", logEventLevelType); + ParameterExpression instanceParam = Expression.Parameter(typeof(object)); + UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); + ParameterExpression callerStackBoundaryDeclaringTypeParam = Expression.Parameter(typeof(Type)); + ParameterExpression levelParam = Expression.Parameter(typeof(object)); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); + MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); + _isEnabledForDelegate = Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam).Compile(); + + // Action Log = + // (logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Write(callerStackBoundaryDeclaringType, level, message, exception); } + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Log", + typeof(Type), + logEventLevelType, + typeof(string), + typeof(Exception)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + var writeMethodExp = Expression.Call( + instanceCast, + writeExceptionMethodInfo, + callerStackBoundaryDeclaringTypeParam, + levelCast, + messageParam, + exceptionParam); + _logDelegate = Expression.Lambda>( + writeMethodExp, + instanceParam, + callerStackBoundaryDeclaringTypeParam, + levelParam, + messageParam, + exceptionParam).Compile(); } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) @@ -882,112 +1192,77 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception return IsLogLevelEnable(logLevel); } - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); - - if (exception != null) + if (!IsLogLevelEnable(logLevel)) { - return LogException(logLevel, messageFunc, exception); + return false; } - switch (logLevel) + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + // determine correct caller - this might change due to jit optimizations with method inlining + if (s_callerStackBoundaryType == null) { - case LogLevel.Info: - if (_logger.IsInfoEnabled) - { - _logger.Info(messageFunc()); - return true; - } - break; - case LogLevel.Warn: - if (_logger.IsWarnEnabled) - { - _logger.Warn(messageFunc()); - return true; - } - break; - case LogLevel.Error: - if (_logger.IsErrorEnabled) - { - _logger.Error(messageFunc()); - return true; - } - break; - case LogLevel.Fatal: - if (_logger.IsFatalEnabled) - { - _logger.Fatal(messageFunc()); - return true; - } - break; - default: - if (_logger.IsDebugEnabled) + lock (CallerStackBoundaryTypeSync) + { +#if !LIBLOG_PORTABLE + StackTrace stack = new StackTrace(); + Type thisType = GetType(); + s_callerStackBoundaryType = Type.GetType("LoggerExecutionWrapper"); + for (var i = 1; i < stack.FrameCount; i++) { - _logger.Debug(messageFunc()); // Log4Net doesn't have a 'Trace' level, so all Trace messages are written as 'Debug' - return true; + if (!IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType)) + { + s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; + break; + } } - break; +#else + s_callerStackBoundaryType = typeof (LoggerExecutionWrapper); +#endif + } } - return false; + + var translatedLevel = TranslateLevel(logLevel); + _logDelegate(_logger, s_callerStackBoundaryType, translatedLevel, messageFunc(), exception); + return true; } - private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) + private static bool IsInTypeHierarchy(Type currentType, Type checkType) { - switch (logLevel) + while (currentType != null && currentType != typeof(object)) { - case LogLevel.Info: - if (_logger.IsDebugEnabled) - { - _logger.Info(messageFunc(), exception); - return true; - } - break; - case LogLevel.Warn: - if (_logger.IsWarnEnabled) - { - _logger.Warn(messageFunc(), exception); - return true; - } - break; - case LogLevel.Error: - if (_logger.IsErrorEnabled) - { - _logger.Error(messageFunc(), exception); - return true; - } - break; - case LogLevel.Fatal: - if (_logger.IsFatalEnabled) - { - _logger.Fatal(messageFunc(), exception); - return true; - } - break; - default: - if (_logger.IsDebugEnabled) - { - _logger.Debug(messageFunc(), exception); - return true; - } - break; + if (currentType == checkType) + { + return true; + } + currentType = currentType.GetBaseTypePortable(); } return false; } private bool IsLogLevelEnable(LogLevel logLevel) + { + var level = TranslateLevel(logLevel); + return _isEnabledForDelegate(_logger, level); + } + + private object TranslateLevel(LogLevel logLevel) { switch (logLevel) { + case LogLevel.Trace: case LogLevel.Debug: - return _logger.IsDebugEnabled; + return _levelDebug; case LogLevel.Info: - return _logger.IsInfoEnabled; + return _levelInfo; case LogLevel.Warn: - return _logger.IsWarnEnabled; + return _levelWarn; case LogLevel.Error: - return _logger.IsErrorEnabled; + return _levelError; case LogLevel.Fatal: - return _logger.IsFatalEnabled; + return _levelFatal; default: - return _logger.IsDebugEnabled; + throw new ArgumentOutOfRangeException("logLevel", logLevel, null); } } } @@ -996,17 +1271,18 @@ private bool IsLogLevelEnable(LogLevel logLevel) internal class EntLibLogProvider : LogProviderBase { private const string TypeTemplate = "Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging"; - private static bool _providerIsAvailableOverride = true; + private static bool s_providerIsAvailableOverride = true; private static readonly Type LogEntryType; private static readonly Type LoggerType; private static readonly Type TraceEventTypeType; private static readonly Action WriteLogEntry; private static readonly Func ShouldLogEntry; + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] static EntLibLogProvider() { - LogEntryType = Type.GetType(string.Format(TypeTemplate, "LogEntry")); - LoggerType = Type.GetType(string.Format(TypeTemplate, "Logger")); + LogEntryType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "LogEntry")); + LoggerType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "Logger")); TraceEventTypeType = TraceEventTypeValues.Type; if (LogEntryType == null || TraceEventTypeType == null @@ -1018,6 +1294,7 @@ static EntLibLogProvider() ShouldLogEntry = GetShouldLogEntry(); } + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "EnterpriseLibrary")] public EntLibLogProvider() { if (!IsLoggerAvailable()) @@ -1028,13 +1305,13 @@ public EntLibLogProvider() public static bool ProviderIsAvailableOverride { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } } - public override ILog GetLogger(string name) + public override Logger GetLogger(string name) { - return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry); + return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry).Log; } internal static bool IsLoggerAvailable() @@ -1057,7 +1334,7 @@ private static Action GetWriteLogEntry() logNameParameter); //Logger.Write(new LogEntry(....)); - MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("Write", new[] { LogEntryType }); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("Write", LogEntryType); var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); return Expression.Lambda>( @@ -1079,7 +1356,7 @@ private static Func GetShouldLogEntry() logNameParameter); //Logger.Write(new LogEntry(....)); - MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("ShouldLog", new[] { LogEntryType }); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("ShouldLog", LogEntryType); var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); return Expression.Lambda>( @@ -1092,22 +1369,22 @@ private static MemberInitExpression GetWriteLogExpression(Expression message, Expression severityParameter, ParameterExpression logNameParameter) { var entryType = LogEntryType; - MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), new [] - { + MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), Expression.Bind(entryType.GetPropertyPortable("Message"), message), Expression.Bind(entryType.GetPropertyPortable("Severity"), severityParameter), - Expression.Bind(entryType.GetPropertyPortable("TimeStamp"), + Expression.Bind( + entryType.GetPropertyPortable("TimeStamp"), Expression.Property(null, typeof (DateTime).GetPropertyPortable("UtcNow"))), - Expression.Bind(entryType.GetPropertyPortable("Categories"), + Expression.Bind( + entryType.GetPropertyPortable("Categories"), Expression.ListInit( Expression.New(typeof (List)), - typeof (List).GetMethodPortable("Add", new[] {typeof (string)}), - logNameParameter)) - }); + typeof (List).GetMethodPortable("Add", typeof (string)), + logNameParameter))); return memberInit; } - public class EntLibLogger : ILog + internal class EntLibLogger { private readonly string _loggerName; private readonly Action _writeLog; @@ -1168,8 +1445,9 @@ private static int MapSeverity(LogLevel logLevel) internal class SerilogLogProvider : LogProviderBase { private readonly Func _getLoggerByNameDelegate; - private static bool _providerIsAvailableOverride = true; + private static bool s_providerIsAvailableOverride = true; + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] public SerilogLogProvider() { if (!IsLoggerAvailable()) @@ -1181,13 +1459,13 @@ public SerilogLogProvider() public static bool ProviderIsAvailableOverride { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } } - public override ILog GetLogger(string name) + public override Logger GetLogger(string name) { - return new SerilogLogger(_getLoggerByNameDelegate(name)); + return new SerilogLogger(_getLoggerByNameDelegate(name)).Log; } internal static bool IsLoggerAvailable() @@ -1209,13 +1487,10 @@ private static Func GetPushProperty() { Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); MethodInfo pushPropertyMethod = ndcContextType.GetMethodPortable( - "PushProperty", - new[] - { - typeof(string), - typeof(object), - typeof(bool) - }); + "PushProperty", + typeof(string), + typeof(object), + typeof(bool)); ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); ParameterExpression destructureObjectParam = Expression.Parameter(typeof(bool), "destructureObjects"); @@ -1240,7 +1515,7 @@ private static Type GetLogManagerType() private static Func GetForContextMethodCall() { Type logManagerType = GetLogManagerType(); - MethodInfo method = logManagerType.GetMethodPortable("ForContext", new[] { typeof(string), typeof(object), typeof(bool) }); + MethodInfo method = logManagerType.GetMethodPortable("ForContext", typeof(string), typeof(object), typeof(bool)); ParameterExpression propertyNameParam = Expression.Parameter(typeof(string), "propertyName"); ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); ParameterExpression destructureObjectsParam = Expression.Parameter(typeof(bool), "destructureObjects"); @@ -1259,7 +1534,7 @@ private static Func GetForContextMethodCall() return name => func("Name", name, false); } - public class SerilogLogger : ILog + internal class SerilogLogger { private readonly object _logger; private static readonly object DebugLevel; @@ -1272,33 +1547,50 @@ public class SerilogLogger : ILog private static readonly Action Write; private static readonly Action WriteException; + [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogEventLevel")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] static SerilogLogger() { - var logEventTypeType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); - DebugLevel = Enum.Parse(logEventTypeType, "Debug", false); - ErrorLevel = Enum.Parse(logEventTypeType, "Error", false); - FatalLevel = Enum.Parse(logEventTypeType, "Fatal", false); - InformationLevel = Enum.Parse(logEventTypeType, "Information", false); - VerboseLevel = Enum.Parse(logEventTypeType, "Verbose", false); - WarningLevel = Enum.Parse(logEventTypeType, "Warning", false); + var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type Serilog.Events.LogEventLevel was not found."); + } + DebugLevel = Enum.Parse(logEventLevelType, "Debug", false); + ErrorLevel = Enum.Parse(logEventLevelType, "Error", false); + FatalLevel = Enum.Parse(logEventLevelType, "Fatal", false); + InformationLevel = Enum.Parse(logEventLevelType, "Information", false); + VerboseLevel = Enum.Parse(logEventLevelType, "Verbose", false); + WarningLevel = Enum.Parse(logEventLevelType, "Warning", false); // Func isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); } var loggerType = Type.GetType("Serilog.ILogger, Serilog"); - var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); + if (loggerType == null) + { + throw new InvalidOperationException("Type Serilog.ILogger was not found."); + } MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabled", logEventLevelType); ParameterExpression instanceParam = Expression.Parameter(typeof(object)); UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); ParameterExpression levelParam = Expression.Parameter(typeof(object)); - UnaryExpression levelCast = Expression.Convert(levelParam, logEventTypeType); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); IsEnabled = Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam).Compile(); // Action Write = // (logger, level, message, params) => { ((SeriLog.ILoggerILogger)logger).Write(level, message, params); } - MethodInfo writeMethodInfo = loggerType.GetMethodPortable("Write", new[] { logEventTypeType, typeof(string), typeof(object[]) }); + MethodInfo writeMethodInfo = loggerType.GetMethodPortable("Write", logEventLevelType, typeof(string), typeof(object[])); ParameterExpression messageParam = Expression.Parameter(typeof(string)); ParameterExpression propertyValuesParam = Expression.Parameter(typeof(object[])); - MethodCallExpression writeMethodExp = Expression.Call(instanceCast, writeMethodInfo, levelCast, messageParam, propertyValuesParam); + MethodCallExpression writeMethodExp = Expression.Call( + instanceCast, + writeMethodInfo, + levelCast, + messageParam, + propertyValuesParam); var expression = Expression.Lambda>( writeMethodExp, instanceParam, @@ -1309,13 +1601,11 @@ static SerilogLogger() // Action WriteException = // (logger, level, exception, message) => { ((ILogger)logger).Write(level, exception, message, new object[]); } - MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Write", new[] - { - logEventTypeType, - typeof(Exception), + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Write", + logEventLevelType, + typeof(Exception), typeof(string), - typeof(object[]) - }); + typeof(object[])); ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); writeMethodExp = Expression.Call( instanceCast, @@ -1468,7 +1758,7 @@ internal delegate void WriteDelegate( params object[] args ); - private static bool _providerIsAvailableOverride = true; + private static bool s_providerIsAvailableOverride = true; private readonly WriteDelegate _logWriteDelegate; public LoupeLogProvider() @@ -1489,13 +1779,13 @@ public LoupeLogProvider() /// public static bool ProviderIsAvailableOverride { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } } - public override ILog GetLogger(string name) + public override Logger GetLogger(string name) { - return new LoupeLogger(name, _logWriteDelegate); + return new LoupeLogger(name, _logWriteDelegate).Log; } public static bool IsLoggerAvailable() @@ -1516,17 +1806,14 @@ private static WriteDelegate GetLogWriteDelegate() MethodInfo method = logManagerType.GetMethodPortable( "Write", - new[] - { - logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), - logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[]) - }); + logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), + logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[])); var callDelegate = (WriteDelegate)method.CreateDelegate(typeof(WriteDelegate)); return callDelegate; } - public class LoupeLogger : ILog + internal class LoupeLogger { private const string LogSystem = "LibLog"; @@ -1538,7 +1825,11 @@ internal LoupeLogger(string category, WriteDelegate logWriteDelegate) { _category = category; _logWriteDelegate = logWriteDelegate; +#if DEBUG + _skipLevel = 2; +#else _skipLevel = 1; +#endif } public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) @@ -1557,7 +1848,7 @@ public bool Log(LogLevel logLevel, Func messageFunc, Exception exception return true; } - private int ToLogMessageSeverity(LogLevel logLevel) + private static int ToLogMessageSeverity(LogLevel logLevel) { switch (logLevel) { @@ -1589,6 +1880,7 @@ internal static class TraceEventTypeValues internal static readonly int Error; internal static readonly int Critical; + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] static TraceEventTypeValues() { var assembly = typeof(Uri).GetAssemblyPortable(); // This is to get to the System.dll assembly in a PCL compatible way. @@ -1606,215 +1898,14 @@ static TraceEventTypeValues() } } - internal class ColouredConsoleLogProvider : LogProviderBase - { - private static readonly Type ConsoleType; - private static readonly Type ConsoleColorType; - private static readonly Action ConsoleWriteLine; - private static readonly Func GetConsoleForeground; - private static readonly Action SetConsoleForeground; - private static bool _providerIsAvailableOverride = true; - private static readonly IDictionary Colors; - - static ColouredConsoleLogProvider() - { - ConsoleType = Type.GetType("System.Console"); - ConsoleColorType = ConsoleColorValues.Type; - - if (!IsLoggerAvailable()) - { - throw new InvalidOperationException("System.Console or System.ConsoleColor type not found"); - } - - MessageFormatter = DefaultMessageFormatter; - Colors = new Dictionary - { - {LogLevel.Fatal, ConsoleColorValues.Red}, - {LogLevel.Error, ConsoleColorValues.Yellow}, - {LogLevel.Warn, ConsoleColorValues.Magenta}, - {LogLevel.Info, ConsoleColorValues.White}, - {LogLevel.Debug, ConsoleColorValues.Gray}, - {LogLevel.Trace, ConsoleColorValues.DarkGray}, - }; - ConsoleWriteLine = GetConsoleWrite(); - GetConsoleForeground = GetGetConsoleForeground(); - SetConsoleForeground = GetSetConsoleForeground(); - } - - internal static bool IsLoggerAvailable() - { - return ProviderIsAvailableOverride && ConsoleType != null && ConsoleColorType != null; - } - - public override ILog GetLogger(string name) - { - return new ColouredConsoleLogger(name, ConsoleWriteLine, GetConsoleForeground, SetConsoleForeground); - } - - /// - /// A delegate returning a formatted log message - /// - /// The name of the Logger - /// The Log Level - /// The Log Message - /// The Exception, if there is one - /// A formatted Log Message string. - internal delegate string MessageFormatterDelegate( - string loggerName, - LogLevel level, - object message, - Exception e); - - internal static MessageFormatterDelegate MessageFormatter { get; set; } - - public static bool ProviderIsAvailableOverride - { - get { return _providerIsAvailableOverride; } - set { _providerIsAvailableOverride = value; } - } - - protected static string DefaultMessageFormatter(string loggerName, LogLevel level, object message, Exception e) - { - var stringBuilder = new StringBuilder(); - stringBuilder.Append(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss", CultureInfo.InvariantCulture)); - stringBuilder.Append(" "); - - // Append a readable representation of the log level - stringBuilder.Append(("[" + level.ToString().ToUpper() + "]").PadRight(8)); - stringBuilder.Append("(" + loggerName + ") "); - - // Append the message - stringBuilder.Append(message); - - // Append stack trace if there is an exception - if (e != null) - { - stringBuilder.Append(Environment.NewLine).Append(e.GetType()); - stringBuilder.Append(Environment.NewLine).Append(e.Message); - stringBuilder.Append(Environment.NewLine).Append(e.StackTrace); - } - - return stringBuilder.ToString(); - } - - private static Action GetConsoleWrite() - { - var messageParameter = Expression.Parameter(typeof(string), "message"); - - MethodInfo writeMethod = ConsoleType.GetMethodPortable("WriteLine", new[] { typeof(string) }); - var writeExpression = Expression.Call(writeMethod, messageParameter); - - return Expression.Lambda>( - writeExpression, messageParameter).Compile(); - } - - private static Func GetGetConsoleForeground() - { - MethodInfo getForeground = ConsoleType.GetPropertyPortable("ForegroundColor").GetGetMethod(); - var getForegroundExpression = Expression.Convert(Expression.Call(getForeground), typeof(int)); - - return Expression.Lambda>(getForegroundExpression).Compile(); - } - - private static Action GetSetConsoleForeground() - { - var colorParameter = Expression.Parameter(typeof(int), "color"); - - MethodInfo setForeground = ConsoleType.GetPropertyPortable("ForegroundColor").GetSetMethod(); - var setForegroundExpression = Expression.Call(setForeground, - Expression.Convert(colorParameter, ConsoleColorType)); - - return Expression.Lambda>( - setForegroundExpression, colorParameter).Compile(); - } - - public class ColouredConsoleLogger : ILog - { - private readonly string _name; - private readonly Action _write; - private readonly Func _getForeground; - private readonly Action _setForeground; - - public ColouredConsoleLogger(string name, Action write, - Func getForeground, Action setForeground) - { - _name = name; - _write = write; - _getForeground = getForeground; - _setForeground = setForeground; - } - - public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, - params object[] formatParameters) - { - if (messageFunc == null) - { - return true; - } - - messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); - - Write(logLevel, messageFunc(), exception); - return true; - } - - protected void Write(LogLevel logLevel, string message, Exception e = null) - { - var formattedMessage = MessageFormatter(this._name, logLevel, message, e); - int color; - - if (Colors.TryGetValue(logLevel, out color)) - { - var originalColor = _getForeground(); - try - { - _setForeground(color); - _write(formattedMessage); - } - finally - { - _setForeground(originalColor); - } - } - else - { - _write(formattedMessage); - } - } - } - - private static class ConsoleColorValues - { - internal static readonly Type Type; - internal static readonly int Red; - internal static readonly int Yellow; - internal static readonly int Magenta; - internal static readonly int White; - internal static readonly int Gray; - internal static readonly int DarkGray; - - static ConsoleColorValues() - { - Type = Type.GetType("System.ConsoleColor"); - if (Type == null) return; - Red = (int)Enum.Parse(Type, "Red", false); - Yellow = (int)Enum.Parse(Type, "Yellow", false); - Magenta = (int)Enum.Parse(Type, "Magenta", false); - White = (int)Enum.Parse(Type, "White", false); - Gray = (int)Enum.Parse(Type, "Gray", false); - DarkGray = (int)Enum.Parse(Type, "DarkGray", false); - } - } - } - internal static class LogMessageFormatter { - private static readonly Regex Pattern = new Regex(@"\{\w{1,}\}"); + private static readonly Regex Pattern = new Regex(@"\{@?\w{1,}\}"); /// /// Some logging frameworks support structured logging, such as serilog. This will allow you to add names to structured data in a format string: /// For example: Log("Log message to {user}", user). This only works with serilog, but as the user of LibLog, you don't know if serilog is actually - /// used. So, this class simulates that. it will replace any text in {curlybraces} with an index number. + /// used. So, this class simulates that. it will replace any text in {curly braces} with an index number. /// /// "Log {message} to {user}" would turn into => "Log {0} to {1}". Then the format parameters are handled using regular .net string.Format. /// @@ -1823,7 +1914,7 @@ internal static class LogMessageFormatter /// public static Func SimulateStructuredLogging(Func messageBuilder, object[] formatParameters) { - if(formatParameters == null) + if (formatParameters == null || formatParameters.Length == 0) { return messageBuilder; } @@ -1843,7 +1934,7 @@ public static Func SimulateStructuredLogging(Func messageBuilder } try { - return String.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters); + return string.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters); } catch (FormatException ex) { @@ -1868,7 +1959,7 @@ internal static class TypeExtensions internal static MethodInfo GetMethodPortable(this Type type, string name) { #if LIBLOG_PORTABLE - return type.GetRuntimeMethod(name, new Type[]{}); + return type.GetRuntimeMethods().SingleOrDefault(m => m.Name == name); #else return type.GetMethod(name); #endif @@ -1892,6 +1983,24 @@ internal static PropertyInfo GetPropertyPortable(this Type type, string name) #endif } + internal static IEnumerable GetFieldsPortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeFields(); +#else + return type.GetFields(); +#endif + } + + internal static Type GetBaseTypePortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().BaseType; +#else + return type.BaseType; +#endif + } + #if LIBLOG_PORTABLE internal static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) { diff --git a/src/ScriptCs.Contracts/LogExtensions.cs b/src/ScriptCs.Contracts/LogExtensions.cs new file mode 100644 index 00000000..59f170c9 --- /dev/null +++ b/src/ScriptCs.Contracts/LogExtensions.cs @@ -0,0 +1,251 @@ +namespace ScriptCs.Contracts +{ + using System; + + public static class LogExtensions + { + public static bool IsDebugEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Debug); + } + + public static bool IsErrorEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Error); + } + + public static bool IsFatalEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Fatal); + } + + public static bool IsInfoEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Info); + } + + public static bool IsTraceEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Trace); + } + + public static bool IsWarnEnabled(this ILog logger) + { + Guard.AgainstNullArgument("logger", logger); + return logger.Log(LogLevel.Warn); + } + + public static void Debug(this ILog logger, Func messageFunc) + { + Guard.AgainstNullArgument("logger", logger); + logger.Log(LogLevel.Debug, messageFunc); + } + + public static void Debug(this ILog logger, string message) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc()); + } + } + + public static void DebugFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsDebugEnabled()) + { + logger.LogFormat(LogLevel.Debug, message, args); + } + } + + public static void DebugException(this ILog logger, string message, Exception exception) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc(), exception); + } + } + + public static void DebugException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc(), exception, formatParams); + } + } + + public static void Error(this ILog logger, Func messageFunc) + { + logger.Log(LogLevel.Error, messageFunc); + } + + public static void Error(this ILog logger, string message) + { + if (logger.IsErrorEnabled()) + { + logger.Log(LogLevel.Error, message.AsFunc()); + } + } + + public static void ErrorFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsErrorEnabled()) + { + logger.LogFormat(LogLevel.Error, message, args); + } + } + + public static void ErrorException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsErrorEnabled()) + { + logger.Log(LogLevel.Error, message.AsFunc(), exception, formatParams); + } + } + + public static void Fatal(this ILog logger, Func messageFunc) + { + logger.Log(LogLevel.Fatal, messageFunc); + } + + public static void Fatal(this ILog logger, string message) + { + if (logger.IsFatalEnabled()) + { + logger.Log(LogLevel.Fatal, message.AsFunc()); + } + } + + public static void FatalFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsFatalEnabled()) + { + logger.LogFormat(LogLevel.Fatal, message, args); + } + } + + public static void FatalException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsFatalEnabled()) + { + logger.Log(LogLevel.Fatal, message.AsFunc(), exception, formatParams); + } + } + + public static void Info(this ILog logger, Func messageFunc) + { + Guard.AgainstNullArgument("logger", logger); + logger.Log(LogLevel.Info, messageFunc); + } + + public static void Info(this ILog logger, string message) + { + if (logger.IsInfoEnabled()) + { + logger.Log(LogLevel.Info, message.AsFunc()); + } + } + + public static void InfoFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsInfoEnabled()) + { + logger.LogFormat(LogLevel.Info, message, args); + } + } + + public static void InfoException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsInfoEnabled()) + { + logger.Log(LogLevel.Info, message.AsFunc(), exception, formatParams); + } + } + + public static void Trace(this ILog logger, Func messageFunc) + { + Guard.AgainstNullArgument("logger", logger); + logger.Log(LogLevel.Trace, messageFunc); + } + + public static void Trace(this ILog logger, string message) + { + if (logger.IsTraceEnabled()) + { + logger.Log(LogLevel.Trace, message.AsFunc()); + } + } + + public static void TraceFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsTraceEnabled()) + { + logger.LogFormat(LogLevel.Trace, message, args); + } + } + + public static void TraceException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsTraceEnabled()) + { + logger.Log(LogLevel.Trace, message.AsFunc(), exception, formatParams); + } + } + + public static void Warn(this ILog logger, Func messageFunc) + { + Guard.AgainstNullArgument("logger", logger); + logger.Log(LogLevel.Warn, messageFunc); + } + + public static void Warn(this ILog logger, string message) + { + if (logger.IsWarnEnabled()) + { + logger.Log(LogLevel.Warn, message.AsFunc()); + } + } + + public static void WarnFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsWarnEnabled()) + { + logger.LogFormat(LogLevel.Warn, message, args); + } + } + + public static void WarnException( + this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsWarnEnabled()) + { + logger.Log(LogLevel.Warn, message.AsFunc(), exception, formatParams); + } + } + + private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args) + { + logger.Log(logLevel, message.AsFunc(), null, args); + } + + // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b + private static Func AsFunc(this T value) where T : class + { + return value.Return; + } + + private static T Return(this T value) + { + return value; + } + } +} diff --git a/src/ScriptCs.Contracts/LogLevel.cs b/src/ScriptCs.Contracts/LogLevel.cs index bd15dd0e..0a59e0c5 100644 --- a/src/ScriptCs.Contracts/LogLevel.cs +++ b/src/ScriptCs.Contracts/LogLevel.cs @@ -2,9 +2,11 @@ { public enum LogLevel { - Error, - Info, + Trace, Debug, - Trace + Info, + Warn, + Error, + Fatal } } diff --git a/src/ScriptCs.Contracts/LogProviderExtensions.cs b/src/ScriptCs.Contracts/LogProviderExtensions.cs new file mode 100644 index 00000000..8bca68bd --- /dev/null +++ b/src/ScriptCs.Contracts/LogProviderExtensions.cs @@ -0,0 +1,67 @@ +namespace ScriptCs.Contracts +{ + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + + public static class LogProviderExtensions + { + public static ILog For(this ILogProvider provider) + { + return provider.For(typeof(T)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ILog ForCurrentType(this ILogProvider provider) + { + return provider.For(new StackFrame(1, false).GetMethod().DeclaringType); + } + + public static ILog For(this ILogProvider provider, Type type) + { + return provider.For(type.FullName); + } + + public static ILog For(this ILogProvider provider, string name) + { + return new LoggerExecutionWrapper(provider.GetLogger(name)); + } + + private class LoggerExecutionWrapper : ILog + { + private const string FailedToGenerateLogMessage = "Failed to generate log message"; + + private readonly Logger _logger; + + internal LoggerExecutionWrapper(Logger logger) + { + _logger = logger; + } + + public bool Log( + LogLevel logLevel, Func createMessage, Exception exception = null, params object[] formatArgs) + { + if (createMessage == null) + { + return _logger(logLevel, null); + } + + Func wrappedMessageFunc = () => + { + try + { + return createMessage(); + } + catch (Exception ex) + { + Log(LogLevel.Error, () => FailedToGenerateLogMessage, ex); + } + + return null; + }; + + return _logger(logLevel, wrappedMessageFunc, exception, formatArgs); + } + } + } +} diff --git a/src/ScriptCs.Contracts/Logger.cs b/src/ScriptCs.Contracts/Logger.cs new file mode 100644 index 00000000..71e8b661 --- /dev/null +++ b/src/ScriptCs.Contracts/Logger.cs @@ -0,0 +1,7 @@ +namespace ScriptCs.Contracts +{ + using System; + + public delegate bool Logger( + LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); +} diff --git a/src/ScriptCs.Contracts/Printers.cs b/src/ScriptCs.Contracts/Printers.cs new file mode 100644 index 00000000..73a8e9ab --- /dev/null +++ b/src/ScriptCs.Contracts/Printers.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; + +namespace ScriptCs.Contracts +{ + public class Printers + { + private readonly IObjectSerializer _serializer; + private readonly Dictionary> _dictionary = new Dictionary>(); + public Printers(IObjectSerializer serializer) + { + _serializer = serializer; + } + + public void AddCustomPrinter(Func printer) + { + _dictionary[typeof(T)] = x => printer((T)x); + } + + private string GetStringFor(Type t, object obj) + { + Func printer; + if (_dictionary.TryGetValue(t, out printer)) + { + return printer(obj); + } + return _serializer.Serialize(obj); + } + + public string GetStringFor(object obj) + { + return GetStringFor(obj.GetType(), obj); + } + + public string GetStringFor(T obj) + { + return GetStringFor(typeof(T), obj); + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Contracts/ProjectItem.cs b/src/ScriptCs.Contracts/ProjectItem.cs new file mode 100644 index 00000000..5a11d254 --- /dev/null +++ b/src/ScriptCs.Contracts/ProjectItem.cs @@ -0,0 +1,16 @@ +using System; + +namespace ScriptCs.Contracts +{ + public class ProjectItem + { + public ProjectItem(Guid project, Guid parent) + { + Project = project; + Parent = parent; + } + + public Guid Parent { get; private set; } + public Guid Project { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Contracts/Properties/AssemblyInfo.cs b/src/ScriptCs.Contracts/Properties/AssemblyInfo.cs deleted file mode 100644 index cb1fda07..00000000 --- a/src/ScriptCs.Contracts/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("ScriptCs.Contracts")] -[assembly: AssemblyDescription("This assembly contains the components necessary to create script packs for scriptcs.")] - -[assembly: Guid("a0cbc203-deff-4b7c-b414-11797b7a3100")] diff --git a/src/ScriptCs.Contracts/ScriptCs.Contracts.csproj b/src/ScriptCs.Contracts/ScriptCs.Contracts.csproj index a68a47b5..8d7c12e5 100644 --- a/src/ScriptCs.Contracts/ScriptCs.Contracts.csproj +++ b/src/ScriptCs.Contracts/ScriptCs.Contracts.csproj @@ -1,118 +1,26 @@ - - - + - Debug - AnyCPU - {6049E205-8B5F-4080-B023-70600E51FD64} - Library - ScriptCs.Contracts - ScriptCs.Contracts - Properties - 512 - v4.5 - - ..\..\ - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset + 1.0.0 + netstandard2.0 + ScriptCs.Contracts + Glenn Block, Filip Wojcieszyn, Justin Rusbatch + https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md + http://scriptcs.net + http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 + ScriptCs.Contracts + ScriptCs.Contracts contains the components necessary to create script packs for scriptcs. + roslyn csx script packs scriptcs + LIBLOG_PROVIDERS_ONLY - - - - - - - - + - - - - Guard.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - - - - - - + + - - + + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/ScriptCs.Contracts/ScriptPackSession.cs b/src/ScriptCs.Contracts/ScriptPackSession.cs index 5355a269..80851347 100644 --- a/src/ScriptCs.Contracts/ScriptPackSession.cs +++ b/src/ScriptCs.Contracts/ScriptPackSession.cs @@ -37,20 +37,11 @@ private void AddScriptContextNamespace() } } - public virtual IEnumerable Contexts - { - get { return _contexts; } - } + public virtual IEnumerable Contexts => _contexts; - public IEnumerable References - { - get { return _references; } - } + public IEnumerable References => _references; - public IEnumerable Namespaces - { - get { return _namespaces; } - } + public IEnumerable Namespaces => _namespaces; public void InitializePacks() { @@ -68,15 +59,9 @@ public void TerminatePacks() } } - public IDictionary State - { - get { return _state; } - } + public IDictionary State => _state; - public string[] ScriptArgs - { - get { return _scriptArgs; } - } + public string[] ScriptArgs => _scriptArgs; void IScriptPackSession.AddReference(string assemblyDisplayNameOrPath) { @@ -87,5 +72,6 @@ void IScriptPackSession.ImportNamespace(string @namespace) { _namespaces.Add(@namespace); } + } } diff --git a/src/ScriptCs.Contracts/ScriptResult.cs b/src/ScriptCs.Contracts/ScriptResult.cs index ba4ee1ee..f02001af 100644 --- a/src/ScriptCs.Contracts/ScriptResult.cs +++ b/src/ScriptCs.Contracts/ScriptResult.cs @@ -57,13 +57,7 @@ public ScriptResult( public ExceptionDispatchInfo CompileExceptionInfo { get; private set; } - public IEnumerable InvalidNamespaces - { - get - { - return _invalidNamespaces.ToArray(); - } - } + public IEnumerable InvalidNamespaces => _invalidNamespaces.ToArray(); public bool IsCompleteSubmission { get; private set; } } diff --git a/src/ScriptCs.Contracts/packages.config b/src/ScriptCs.Contracts/packages.config deleted file mode 100644 index 6e34c137..00000000 --- a/src/ScriptCs.Contracts/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/ScriptCs.Core/AppDomainAssemblyResolver.cs b/src/ScriptCs.Core/AppDomainAssemblyResolver.cs index bf41c866..925420fc 100644 --- a/src/ScriptCs.Core/AppDomainAssemblyResolver.cs +++ b/src/ScriptCs.Core/AppDomainAssemblyResolver.cs @@ -1,5 +1,4 @@ -using ScriptCs.Logging; -using System; +using System; using System.Collections.Generic; using System.Reflection; using ScriptCs.Contracts; @@ -16,21 +15,21 @@ public class AppDomainAssemblyResolver : IAppDomainAssemblyResolver private readonly IDictionary _assemblyInfoMap; public AppDomainAssemblyResolver( - ILog logger, + ILogProvider logProvider, IFileSystem fileSystem, IAssemblyResolver resolver, IAssemblyUtility assemblyUtility, IDictionary assemblyInfoMap = null, Func resolveHandler = null) { - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgument("resolver", resolver); Guard.AgainstNullArgument("assemblyUtility", assemblyUtility); _assemblyInfoMap = assemblyInfoMap ?? new Dictionary(); _assemblyUtility = assemblyUtility; - _logger = logger; + _logger = logProvider.ForCurrentType(); _fileSystem = fileSystem; _resolver = resolver; diff --git a/src/ScriptCs.Core/AssemblyResolver.cs b/src/ScriptCs.Core/AssemblyResolver.cs index 69c5349e..d3a0f058 100644 --- a/src/ScriptCs.Core/AssemblyResolver.cs +++ b/src/ScriptCs.Core/AssemblyResolver.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -19,7 +18,7 @@ public AssemblyResolver( IFileSystem fileSystem, IPackageAssemblyResolver packageAssemblyResolver, IAssemblyUtility assemblyUtility, - ILog logger) + ILogProvider logProvider) { Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgumentProperty("fileSystem", "PackagesFolder", fileSystem.PackagesFolder); @@ -27,12 +26,12 @@ public AssemblyResolver( Guard.AgainstNullArgument("packageAssemblyResolver", packageAssemblyResolver); Guard.AgainstNullArgument("assemblyUtility", assemblyUtility); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); _fileSystem = fileSystem; _packageAssemblyResolver = packageAssemblyResolver; _assemblyUtility = assemblyUtility; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public IEnumerable GetAssemblyPaths(string path, bool binariesOnly = false) diff --git a/src/ScriptCs.Core/AssemblyUtility.cs b/src/ScriptCs.Core/AssemblyUtility.cs index 5e1b1f9c..80e37c0f 100644 --- a/src/ScriptCs.Core/AssemblyUtility.cs +++ b/src/ScriptCs.Core/AssemblyUtility.cs @@ -19,19 +19,10 @@ public bool IsManagedAssembly(string path) } } - public Assembly LoadFile(string path) - { - return Assembly.LoadFile(path); - } + public Assembly LoadFile(string path) => Assembly.LoadFile(path); - public Assembly Load(AssemblyName assemblyRef) - { - return Assembly.Load(assemblyRef); - } + public Assembly Load(AssemblyName assemblyRef) => Assembly.Load(assemblyRef); - public AssemblyName GetAssemblyName(string path) - { - return AssemblyName.GetAssemblyName(path); - } + public AssemblyName GetAssemblyName(string path) => AssemblyName.GetAssemblyName(path); } } diff --git a/src/ScriptCs.Core/DebugScriptExecutor.cs b/src/ScriptCs.Core/DebugScriptExecutor.cs index 90c72db4..b9764f8a 100644 --- a/src/ScriptCs.Core/DebugScriptExecutor.cs +++ b/src/ScriptCs.Core/DebugScriptExecutor.cs @@ -1,12 +1,12 @@ -using ScriptCs.Contracts; -using ScriptCs.Logging; +using System; +using ScriptCs.Contracts; namespace ScriptCs { public class DebugScriptExecutor : ScriptExecutor { - public DebugScriptExecutor(IFileSystem fileSystem, IFilePreProcessor filePreProcessor, IScriptEngine scriptEngine, ILog logger, IScriptLibraryComposer composer) - : base(fileSystem, filePreProcessor, scriptEngine, logger, composer) + public DebugScriptExecutor(IFileSystem fileSystem, IFilePreProcessor filePreProcessor, IScriptEngine scriptEngine, ILogProvider logProvider, IScriptLibraryComposer composer, IScriptInfo scriptInfo) + : base(fileSystem, filePreProcessor, scriptEngine, logProvider, composer, scriptInfo) { } } diff --git a/src/ScriptCs.Core/Exceptions/ScriptPackException.cs b/src/ScriptCs.Core/Exceptions/ScriptPackException.cs new file mode 100644 index 00000000..7c98d5e9 --- /dev/null +++ b/src/ScriptCs.Core/Exceptions/ScriptPackException.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.Serialization; + +namespace ScriptCs.Exceptions +{ + [Serializable] + public class ScriptPackException : Exception + { + public ScriptPackException(string message) + : base(message) + { + } + + protected ScriptPackException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/ScriptCs.Core/FilePreProcessor.cs b/src/ScriptCs.Core/FilePreProcessor.cs index 5e5674b4..044d4ec0 100644 --- a/src/ScriptCs.Core/FilePreProcessor.cs +++ b/src/ScriptCs.Core/FilePreProcessor.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -12,17 +11,19 @@ public class FilePreProcessor : IFilePreProcessor private readonly ILog _logger; private readonly IEnumerable _lineProcessors; + private readonly IEnumerable _directiveLineProcessors; private readonly IFileSystem _fileSystem; - public FilePreProcessor(IFileSystem fileSystem, ILog logger, IEnumerable lineProcessors) + public FilePreProcessor(IFileSystem fileSystem, ILogProvider logProvider, IEnumerable lineProcessors) { Guard.AgainstNullArgument("fileSystem", fileSystem); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); _fileSystem = fileSystem; - _logger = logger; + _logger = logProvider.ForCurrentType(); _lineProcessors = lineProcessors; + _directiveLineProcessors = _lineProcessors.OfType(); } public virtual FilePreProcessorResult ProcessFile(string path) @@ -55,7 +56,8 @@ protected virtual FilePreProcessorResult Process(Action parse Namespaces = context.Namespaces, LoadedScripts = context.LoadedScripts, References = context.References, - Code = code + Code = code, + ScriptPath = context.ScriptPath }; } @@ -81,8 +83,15 @@ public virtual void ParseFile(string path, FileParserContext context) _logger.DebugFormat("Processing {0}...", filename); - // Add script to loaded collection before parsing to avoid loop. - context.LoadedScripts.Add(fullPath); + if (context.ScriptPath == null) + { + context.ScriptPath = fullPath; + } + else + { + // Add script to loaded collection before parsing to avoid loop. + context.LoadedScripts.Add(fullPath); + } var scriptLines = _fileSystem.ReadFileLines(fullPath).ToList(); @@ -103,7 +112,6 @@ public virtual void ParseScript(List scriptLines, FileParserContext cont var isBeforeCode = index < codeIndex || codeIndex < 0; var wasProcessed = _lineProcessors.Any(x => x.ProcessLine(this, context, line, isBeforeCode)); - if (!wasProcessed) { context.BodyLines.Add(line); @@ -137,10 +145,11 @@ private void InDirectory(string path, Action action) private bool IsNonDirectiveLine(string line) { - var directiveLineProcessors = - _lineProcessors.OfType(); + line = line.Trim(); + if (line.StartsWith("//") || line.Equals(string.Empty)) + return false; - return line.Trim() != string.Empty && !directiveLineProcessors.Any(lp => lp.Matches(line)); + return !_directiveLineProcessors.Any(lp => lp.Matches(line)); } private static bool IsUsingLine(string line) diff --git a/src/ScriptCs.Core/FileSystem.cs b/src/ScriptCs.Core/FileSystem.cs index 5d11d484..6bd0ef84 100644 --- a/src/ScriptCs.Core/FileSystem.cs +++ b/src/ScriptCs.Core/FileSystem.cs @@ -89,14 +89,11 @@ public virtual bool IsPathRooted(string path) public virtual string CurrentDirectory { - get { return Environment.CurrentDirectory; } - set { Environment.CurrentDirectory = value; } + get => Environment.CurrentDirectory; + set => Environment.CurrentDirectory = value; } - public virtual string NewLine - { - get { return Environment.NewLine; } - } + public virtual string NewLine => Environment.NewLine; public virtual DateTime GetLastWriteTime(string file) { @@ -145,14 +142,8 @@ public virtual void WriteAllBytes(string filePath, byte[] bytes) File.WriteAllBytes(filePath, bytes); } - public virtual string GlobalFolder - { - get - { - return Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "scriptcs"); - } - } + public virtual string GlobalFolder => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "scriptcs"); public virtual string GetWorkingDirectory(string path) { @@ -181,39 +172,21 @@ public virtual string GetFullPath(string path) return Path.GetFullPath(path); } - public virtual string HostBin - { - get { return AppDomain.CurrentDomain.BaseDirectory; } - } - public virtual string BinFolder - { - get { return "scriptcs_bin"; } - } + public virtual string TempPath => Path.GetTempPath(); - public virtual string DllCacheFolder - { - get { return ".scriptcs_cache"; } - } + public virtual string HostBin => AppDomain.CurrentDomain.BaseDirectory; - public virtual string PackagesFile - { - get { return "scriptcs_packages.config"; } - } + public virtual string BinFolder => "scriptcs_bin"; - public virtual string PackagesFolder - { - get { return "scriptcs_packages"; } - } + public virtual string DllCacheFolder => ".scriptcs_cache"; - public virtual string NugetFile - { - get { return "scriptcs_nuget.config"; } - } + public virtual string PackagesFile => "scriptcs_packages.config"; - public virtual string GlobalOptsFile - { - get { return Path.Combine(GlobalFolder, Constants.ConfigFilename); } - } + public virtual string PackagesFolder => "scriptcs_packages"; + + public virtual string NugetFile => "scriptcs_nuget.config"; + + public virtual string GlobalOptsFile => Path.Combine(GlobalFolder, Constants.ConfigFilename); } } diff --git a/src/ScriptCs.Core/FileSystemMigrator.cs b/src/ScriptCs.Core/FileSystemMigrator.cs deleted file mode 100644 index af765018..00000000 --- a/src/ScriptCs.Core/FileSystemMigrator.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using ScriptCs.Contracts; -using ScriptCs.Logging; - -namespace ScriptCs -{ - public class FileSystemMigrator : IFileSystemMigrator - { - private readonly IFileSystem _fileSystem; - private readonly ILog _logger; - private readonly Dictionary _fileCopies; - private readonly Dictionary _directoryMoves; - private readonly Dictionary _directoryCopies; - - public FileSystemMigrator(IFileSystem fileSystem, ILog logger) - { - Guard.AgainstNullArgument("fileSystem", fileSystem); - Guard.AgainstNullArgument("logger", logger); - - _fileSystem = fileSystem; - _logger = logger; - - _fileCopies = new Dictionary - { - { "packages.config", _fileSystem.PackagesFile }, - { "nuget.config", _fileSystem.NugetFile }, - }; - - _directoryMoves = new Dictionary - { - { ".cache", _fileSystem.DllCacheFolder }, - }; - - _directoryCopies = new Dictionary - { - { "bin", _fileSystem.BinFolder }, - { "packages", _fileSystem.PackagesFolder }, - }; - } - - public void Migrate() - { - foreach (var copy in _fileCopies - .Where(copy => _fileSystem.FileExists(copy.Value))) - { - _logger.DebugFormat( - "Not performing migration since file '{0}' already exists.", - copy.Value); - - return; - } - - foreach (var action in _directoryMoves.Concat(_directoryCopies) - .Where(action => _fileSystem.DirectoryExists(action.Value))) - { - _logger.DebugFormat( - "Not performing migration since directory '{0}' already exists.", - action.Value); - - return; - } - - foreach (var copy in _fileCopies - .Where(copy => _fileSystem.FileExists(copy.Key))) - { - _logger.InfoFormat( - "Copying file '{0}' to '{1}'...", copy.Key, copy.Value); - - _fileSystem.Copy(copy.Key, copy.Value, false); - } - - foreach (var move in _directoryMoves - .Where(move => _fileSystem.DirectoryExists(move.Key))) - { - _logger.InfoFormat( - "Moving directory '{0}' to '{1}'...", move.Key, move.Value); - - _fileSystem.MoveDirectory(move.Key, move.Value); - } - - foreach (var copy in _directoryCopies - .Where(copy => _fileSystem.DirectoryExists(copy.Key))) - { - _logger.InfoFormat( - "Copying directory '{0}' to '{1}'...", copy.Key, copy.Value); - - _fileSystem.CopyDirectory(copy.Key, copy.Value, false); - } - } - } -} diff --git a/src/ScriptCs.Core/FrameworkUtils.cs b/src/ScriptCs.Core/FrameworkUtils.cs new file mode 100644 index 00000000..6a1935fc --- /dev/null +++ b/src/ScriptCs.Core/FrameworkUtils.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptCs +{ + public static class FrameworkUtils + { + private static string _frameworkName; + public static string FrameworkName + { + get + { + if (_frameworkName == null) + { + // in order to handle the weird behavior of old nuget packages + // with NET Standard 2.0, we'll use the entry assembly if possible + var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); + + //Thanks to Dave Glick for this code contribution + var frameworkName = assembly.GetCustomAttributes(true) + .OfType() + .Select(x => x.FrameworkName) + .FirstOrDefault(); + _frameworkName = frameworkName; + } + return _frameworkName; + } + } + } +} diff --git a/src/ScriptCs.Core/ILoggerConfigurator.cs b/src/ScriptCs.Core/ILoggerConfigurator.cs index ff129746..a10efab0 100644 --- a/src/ScriptCs.Core/ILoggerConfigurator.cs +++ b/src/ScriptCs.Core/ILoggerConfigurator.cs @@ -1,14 +1,10 @@ -using ScriptCs.Contracts; -using ScriptCs.Logging; +using System; +using ScriptCs.Contracts; namespace ScriptCs { public interface ILoggerConfigurator { void Configure(IConsole console); - - void Configure(IConsole console, ILog log); - - ILog GetLogger(); } -} \ No newline at end of file +} diff --git a/src/ScriptCs.Core/LoadLineProcessor.cs b/src/ScriptCs.Core/LoadLineProcessor.cs index 733ab6d1..8313b943 100644 --- a/src/ScriptCs.Core/LoadLineProcessor.cs +++ b/src/ScriptCs.Core/LoadLineProcessor.cs @@ -19,15 +19,9 @@ public LoadLineProcessor(IFileSystem fileSystem) _fileSystem = fileSystem; } - protected override string DirectiveName - { - get { return "load"; } - } + protected override string DirectiveName => "load"; - protected override BehaviorAfterCode BehaviorAfterCode - { - get { return BehaviorAfterCode.Throw; } - } + protected override BehaviorAfterCode BehaviorAfterCode => BehaviorAfterCode.Throw; protected override bool ProcessLine(IFileParser parser, FileParserContext context, string line) { diff --git a/src/ScriptCs.Core/NullScriptLibraryComposer.cs b/src/ScriptCs.Core/NullScriptLibraryComposer.cs index 919ffd50..a4b637d2 100644 --- a/src/ScriptCs.Core/NullScriptLibraryComposer.cs +++ b/src/ScriptCs.Core/NullScriptLibraryComposer.cs @@ -9,9 +9,6 @@ public void Compose(string workingDirectory, StringBuilder builder = null) { } - public string ScriptLibrariesFile - { - get { return string.Empty; } - } + public string ScriptLibrariesFile => string.Empty; } } diff --git a/src/ScriptCs.Core/PackageAssemblyResolver.cs b/src/ScriptCs.Core/PackageAssemblyResolver.cs index 75b5088f..860344ce 100644 --- a/src/ScriptCs.Core/PackageAssemblyResolver.cs +++ b/src/ScriptCs.Core/PackageAssemblyResolver.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -16,19 +16,19 @@ public class PackageAssemblyResolver : IPackageAssemblyResolver private List _topLevelPackages; public PackageAssemblyResolver( - IFileSystem fileSystem, IPackageContainer packageContainer, ILog logger, IAssemblyUtility assemblyUtility) + IFileSystem fileSystem, IPackageContainer packageContainer, ILogProvider logProvider, IAssemblyUtility assemblyUtility) { Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgumentProperty("fileSystem", "PackagesFolder", fileSystem.PackagesFolder); Guard.AgainstNullArgumentProperty("fileSystem", "PackagesFile", fileSystem.PackagesFile); Guard.AgainstNullArgument("packageContainer", packageContainer); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); Guard.AgainstNullArgument("assemblyUtility", assemblyUtility); _fileSystem = fileSystem; _packageContainer = packageContainer; - _logger = logger; + _logger = logProvider.ForCurrentType(); _assemblyUtility = assemblyUtility; } diff --git a/src/ScriptCs.Core/Properties/AssemblyInfo.cs b/src/ScriptCs.Core/Properties/AssemblyInfo.cs index ec1ee9d4..8a1cb333 100644 --- a/src/ScriptCs.Core/Properties/AssemblyInfo.cs +++ b/src/ScriptCs.Core/Properties/AssemblyInfo.cs @@ -3,11 +3,5 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("ScriptCs.Core")] -[assembly: AssemblyDescription("ScriptCs.Core is the core framework assembly for scriptcs.")] - -[assembly: Guid("4c4ebd22-f4b0-47de-a417-f0a2a127508c")] - [assembly: InternalsVisibleTo("ScriptCs.Core.Tests")] -[assembly: InternalsVisibleTo("ScriptCs.Engine.Roslyn.Tests")] -[assembly: InternalsVisibleTo("ScriptCs.Engine.Mono.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("ScriptCs.Engine.Roslyn.Tests")] \ No newline at end of file diff --git a/src/ScriptCs.Core/ReferenceLineProcessor.cs b/src/ScriptCs.Core/ReferenceLineProcessor.cs index 2e9090ec..52ccc633 100644 --- a/src/ScriptCs.Core/ReferenceLineProcessor.cs +++ b/src/ScriptCs.Core/ReferenceLineProcessor.cs @@ -19,15 +19,9 @@ public ReferenceLineProcessor(IFileSystem fileSystem) _fileSystem = fileSystem; } - protected override string DirectiveName - { - get { return "r"; } - } + protected override string DirectiveName => "r"; - protected override BehaviorAfterCode BehaviorAfterCode - { - get { return BehaviorAfterCode.Throw; } - } + protected override BehaviorAfterCode BehaviorAfterCode => BehaviorAfterCode.Throw; protected override bool ProcessLine(IFileParser parser, FileParserContext context, string line) { diff --git a/src/ScriptCs.Core/Repl.cs b/src/ScriptCs.Core/Repl.cs index 60e9e101..9b9ddb35 100644 --- a/src/ScriptCs.Core/Repl.cs +++ b/src/ScriptCs.Core/Repl.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -13,24 +12,31 @@ public class Repl : ScriptExecutor, IRepl private readonly string[] _scriptArgs; private readonly IObjectSerializer _serializer; + private readonly Printers _printers; + private readonly ILog _log; public Repl( string[] scriptArgs, IFileSystem fileSystem, IScriptEngine scriptEngine, IObjectSerializer serializer, - ILog logger, + ILogProvider logProvider, IScriptLibraryComposer composer, IConsole console, IFilePreProcessor filePreProcessor, - IEnumerable replCommands) - : base(fileSystem, filePreProcessor, scriptEngine, logger, composer) + IEnumerable replCommands, + Printers printers, + IScriptInfo scriptInfo) + : base(fileSystem, filePreProcessor, scriptEngine, logProvider, composer, scriptInfo) { - Guard.AgainstNullArgument("console", console); Guard.AgainstNullArgument("serializer", serializer); - + Guard.AgainstNullArgument("logProvider", logProvider); + Guard.AgainstNullArgument("console", console); + _scriptArgs = scriptArgs; _serializer = serializer; + _printers = printers; + _log = logProvider.ForCurrentType(); Console = console; Commands = replCommands != null ? replCommands.Where(x => x.CommandName != null).ToDictionary(x => x.CommandName, x => x) : new Dictionary(); } @@ -44,7 +50,7 @@ public Repl( public override void Terminate() { base.Terminate(); - Logger.Debug("Exiting console"); + _log.Debug("Exiting console"); Console.Exit(); } @@ -107,7 +113,7 @@ public override ScriptResult Execute(string script, params string[] scriptArgs) } Console.ForegroundColor = ConsoleColor.Cyan; - + InjectScriptLibraries(FileSystem.CurrentDirectory, preProcessResult, ScriptPackSession.State); Buffer = (Buffer == null) @@ -146,9 +152,7 @@ public override ScriptResult Execute(string script, params string[] scriptArgs) { Console.ForegroundColor = ConsoleColor.Yellow; - var serializedResult = _serializer.Serialize(result.ReturnValue); - - Console.WriteLine(serializedResult); + Console.WriteLine(_printers.GetStringFor(result.ReturnValue)); } Buffer = null; diff --git a/src/ScriptCs.Core/ReplCommands/AliasCommand.cs b/src/ScriptCs.Core/ReplCommands/AliasCommand.cs index ec8e528e..fd46b923 100644 --- a/src/ScriptCs.Core/ReplCommands/AliasCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/AliasCommand.cs @@ -17,15 +17,9 @@ public AliasCommand(IConsole console) _console = console; } - public string Description - { - get { return "Allows you to alias a command with a custom name"; } - } + public string Description => "Allows you to alias a command with a custom name"; - public string CommandName - { - get { return "alias"; } - } + public string CommandName => "alias"; public object Execute(IRepl repl, object[] args) { @@ -51,8 +45,8 @@ public object Execute(IRepl repl, object[] args) return null; } - IReplCommand command; - if (!repl.Commands.TryGetValue(commandName, out command)) + + if (!repl.Commands.TryGetValue(commandName, out var command)) { var message = string.Format( CultureInfo.InvariantCulture, "There is no command named or aliased \"{0}\".", alias); diff --git a/src/ScriptCs.Core/ReplCommands/CdCommand.cs b/src/ScriptCs.Core/ReplCommands/CdCommand.cs index bc6e18c7..86010326 100644 --- a/src/ScriptCs.Core/ReplCommands/CdCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/CdCommand.cs @@ -5,15 +5,9 @@ namespace ScriptCs.ReplCommands { public class CdCommand : IReplCommand { - public string Description - { - get { return "Changes the working directory to the path provided."; } - } + public string Description => "Changes the working directory to the path provided."; - public string CommandName - { - get { return "cd"; } - } + public string CommandName => "cd"; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/ClearCommand.cs b/src/ScriptCs.Core/ReplCommands/ClearCommand.cs index 4a371cb8..a7ede226 100644 --- a/src/ScriptCs.Core/ReplCommands/ClearCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/ClearCommand.cs @@ -6,10 +6,7 @@ public class ClearCommand : IReplCommand { private readonly IConsole _console; - public string Description - { - get { return "Clears the console window."; } - } + public string Description => "Clears the console window."; public ClearCommand(IConsole console) { @@ -18,10 +15,7 @@ public ClearCommand(IConsole console) _console = console; } - public string CommandName - { - get { return "clear"; } - } + public string CommandName => "clear"; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/CwdCommand.cs b/src/ScriptCs.Core/ReplCommands/CwdCommand.cs index fe1b35d2..e193347d 100644 --- a/src/ScriptCs.Core/ReplCommands/CwdCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/CwdCommand.cs @@ -14,15 +14,9 @@ public CwdCommand(IConsole console) _console = console; } - public string Description - { - get { return "Displays the current working directory."; } - } + public string Description => "Displays the current working directory."; - public string CommandName - { - get { return "cwd"; } - } + public string CommandName => "cwd"; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/ExitCommand.cs b/src/ScriptCs.Core/ReplCommands/ExitCommand.cs index 9c21acf2..81c3e6e8 100644 --- a/src/ScriptCs.Core/ReplCommands/ExitCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/ExitCommand.cs @@ -6,15 +6,9 @@ public class ExitCommand : IReplCommand { private readonly IConsole _console; - public string Description - { - get { return "Exits the REPL"; } - } + public string Description => "Exits the REPL"; - public string CommandName - { - get { return "exit"; } - } + public string CommandName => "exit"; public ExitCommand(IConsole console) { @@ -32,8 +26,7 @@ public object Execute(IRepl repl, object[] args) while (!responseIsValid) { - _console.Write("Are you sure you wish to exit? (y/n): "); - response = (_console.ReadLine() ?? string.Empty).ToLowerInvariant(); + response = (_console.ReadLine("Are you sure you wish to exit? (y/n):") ?? string.Empty).ToLowerInvariant(); responseIsValid = response == "y" || response == "n"; } diff --git a/src/ScriptCs.Core/ReplCommands/HelpCommand.cs b/src/ScriptCs.Core/ReplCommands/HelpCommand.cs index 0692bdf6..508464f8 100644 --- a/src/ScriptCs.Core/ReplCommands/HelpCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/HelpCommand.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Text; using ScriptCs.Contracts; namespace ScriptCs.ReplCommands @@ -14,27 +15,102 @@ public HelpCommand(IConsole console) _console = console; } - public string Description - { - get { return "Shows this help."; } - } + public string Description => "Shows this help."; - public string CommandName - { - get { return "help"; } - } + public string CommandName => "help"; public object Execute(IRepl repl, object[] args) { Guard.AgainstNullArgument("repl", repl); - _console.WriteLine("The following commands are available in the REPL:"); + _console.WriteLine("\nThe following commands are available in the REPL:"); foreach (var command in repl.Commands.OrderBy(x => x.Key)) { - _console.WriteLine(string.Format(":{0,-15}{1,10}", command.Key, command.Value.Description)); + string key = string.Format(" :{0,-15} - ", command.Key); + + // make sure we have a good width for formatting purposes + int descWidth = _console.Width - key.Length - 1; + if (descWidth > 25) + { + _console.WriteLine(string.Format("{0}{1,10}", key, WrapTextToColumn(command.Value.Description, descWidth, indentWidth: key.Length))); + } + else + { + // safe-guard: just in the case we have a really long Repl Command "key" + // and a really narrow console width don't wrap the description + // note: the extra newline if to at least make somewhat readable + _console.WriteLine(string.Format("{0}{1,10}\n", key, command.Value.Description)); + } } + _console.WriteLine(string.Empty); return null; } + + /// + /// Word wrap text to specified column width. + /// + /// Unformatted text. + /// Size of the column width. + /// Indentation width when the text is wrap. The first line is not indented. + /// First line indent width. + /// Formatted text. + /// In the future, I believe this method will be moved into some sort of formatting helper class. + private string WrapTextToColumn(string text, int columnWidth, int indentWidth = 0, int initialWidth = 0) + { + // check the initial width + if ((initialWidth < 0) || (initialWidth > (indentWidth + columnWidth))) + { + throw new System.ArgumentOutOfRangeException("initialWidth"); + } + + // TODO: Add additional parameter error checking + + StringBuilder paragraph = new StringBuilder(text.Trim()); + + // add the initial space to text + paragraph.Insert(0, " ", initialWidth); + + if (paragraph.Length > (columnWidth)) + { + int pos = columnWidth; + int backSearchLimit = initialWidth; + do + { + // find a whitespace we can wrap the description line + int savedPos = pos; + while (!char.IsWhiteSpace(paragraph[pos])) + { + pos--; + + // guard against not finding a natural whitespace + // don't go below the spaces we create (indent) + if (pos < backSearchLimit) + { + pos = savedPos; + break; + } + } + + if (char.IsWhiteSpace(paragraph[pos])) + { + paragraph.Remove(pos, 1); // remove the whitespace we found + } + // inject a newline + paragraph.Insert(pos, System.Environment.NewLine); + pos += System.Environment.NewLine.Length; + paragraph.Insert(pos, " ", indentWidth); + pos += indentWidth; + + // prevent searching for whitespace to go below the spaces we put in + backSearchLimit = pos; + + pos += columnWidth; + } while (pos < paragraph.Length); + + } + + return paragraph.ToString(); + } } } \ No newline at end of file diff --git a/src/ScriptCs.Core/ReplCommands/InstallCommand.cs b/src/ScriptCs.Core/ReplCommands/InstallCommand.cs index c269cc02..e668c82c 100644 --- a/src/ScriptCs.Core/ReplCommands/InstallCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/InstallCommand.cs @@ -1,7 +1,7 @@ -using System.Linq; +using System; +using System.Linq; using System.Runtime.Versioning; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.ReplCommands { @@ -15,29 +15,23 @@ public class InstallCommand : IReplCommand public InstallCommand( IPackageInstaller packageInstaller, IPackageAssemblyResolver packageAssemblyResolver, - ILog logger, + ILogProvider logProvider, IInstallationProvider installationProvider) { Guard.AgainstNullArgument("packageInstaller", packageInstaller); Guard.AgainstNullArgument("packageAssemblyResolver", packageAssemblyResolver); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); Guard.AgainstNullArgument("installationProvider", installationProvider); _packageInstaller = packageInstaller; _packageAssemblyResolver = packageAssemblyResolver; - _logger = logger; + _logger = logProvider.ForCurrentType(); _installationProvider = installationProvider; } - public string Description - { - get { return "Installs a Nuget package. I.e. :install "; } - } + public string Description => "Installs a Nuget package. I.e. :install "; - public string CommandName - { - get { return "install"; } - } + public string CommandName => "install"; public object Execute(IRepl repl, object[] args) { @@ -61,7 +55,7 @@ public object Execute(IRepl repl, object[] args) _installationProvider.Initialize(); var packageRef = new PackageReference( - args[0].ToString(), new FrameworkName(".NETFramework,Version=v4.0"), version); + args[0].ToString(), new FrameworkName(".NETFramework,Version=v4.6.1"), version); _packageInstaller.InstallPackages(new[] { packageRef }, allowPre); _packageAssemblyResolver.SavePackages(); diff --git a/src/ScriptCs.Core/ReplCommands/ReferencesCommand.cs b/src/ScriptCs.Core/ReplCommands/ReferencesCommand.cs index f27892dd..668b40d7 100644 --- a/src/ScriptCs.Core/ReplCommands/ReferencesCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/ReferencesCommand.cs @@ -5,15 +5,9 @@ namespace ScriptCs.ReplCommands { public class ReferencesCommand : IReplCommand { - public string CommandName - { - get { return "references"; } - } + public string CommandName => "references"; - public string Description - { - get { return "Displays a list of assemblies referenced from the REPL context."; } - } + public string Description => "Displays a list of assemblies referenced from the REPL context."; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/ResetCommand.cs b/src/ScriptCs.Core/ReplCommands/ResetCommand.cs index 6296d213..8e68ec56 100644 --- a/src/ScriptCs.Core/ReplCommands/ResetCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/ResetCommand.cs @@ -4,15 +4,9 @@ namespace ScriptCs.ReplCommands { public class ResetCommand : IReplCommand { - public string Description - { - get { return "Resets the REPL state. All local variables and member definitions are cleared."; } - } + public string Description => "Resets the REPL state. All local variables and member definitions are cleared."; - public string CommandName - { - get { return "reset"; } - } + public string CommandName => "reset"; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/ScriptPacksCommand.cs b/src/ScriptCs.Core/ReplCommands/ScriptPacksCommand.cs index d2e741d9..b5b9a08d 100644 --- a/src/ScriptCs.Core/ReplCommands/ScriptPacksCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/ScriptPacksCommand.cs @@ -17,18 +17,13 @@ public ScriptPacksCommand(IConsole console) _console = console; } - public string Description - { - get { return "Displays information about script packs available in the REPL session"; } - } + public string Description => "Displays information about script packs available in the REPL session"; - public string CommandName - { - get { return "scriptpacks"; } - } + public string CommandName => "scriptpacks"; public object Execute(IRepl repl, object[] args) { + Guard.AgainstNullArgument("repl", repl); var packContexts = repl.ScriptPackSession.Contexts; if (packContexts.IsNullOrEmpty()) diff --git a/src/ScriptCs.Core/ReplCommands/UsingsCommand.cs b/src/ScriptCs.Core/ReplCommands/UsingsCommand.cs index 89e8eb6e..1548b551 100644 --- a/src/ScriptCs.Core/ReplCommands/UsingsCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/UsingsCommand.cs @@ -5,15 +5,9 @@ namespace ScriptCs.ReplCommands { public class UsingsCommand : IReplCommand { - public string Description - { - get { return "Displays a list of namespaces imported into REPL context."; } - } + public string Description => "Displays a list of namespaces imported into REPL context."; - public string CommandName - { - get { return "usings"; } - } + public string CommandName => "usings"; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ReplCommands/VarsCommand.cs b/src/ScriptCs.Core/ReplCommands/VarsCommand.cs index d5d672c3..87594aab 100644 --- a/src/ScriptCs.Core/ReplCommands/VarsCommand.cs +++ b/src/ScriptCs.Core/ReplCommands/VarsCommand.cs @@ -4,15 +4,9 @@ namespace ScriptCs.ReplCommands { public class VarsCommand : IReplCommand { - public string CommandName - { - get { return "vars"; } - } + public string CommandName => "vars"; - public string Description - { - get { return "Displays a list of variables defined within the REPL, along with their types and values."; } - } + public string Description => "Displays a list of variables defined within the REPL, along with their types and values."; public object Execute(IRepl repl, object[] args) { diff --git a/src/ScriptCs.Core/ScriptCs.Core.csproj b/src/ScriptCs.Core/ScriptCs.Core.csproj index fa50c353..a83e1dc5 100644 --- a/src/ScriptCs.Core/ScriptCs.Core.csproj +++ b/src/ScriptCs.Core/ScriptCs.Core.csproj @@ -1,127 +1,17 @@ - - - + - Debug - AnyCPU - {E590E710-E159-48E6-A3E6-1A83D3FE732C} - Library - ScriptCs - ScriptCs.Core - Properties - 512 - v4.5 - - ..\..\ - 414 + 1.0.0 + netstandard2.0 + ScriptCs.Core + Glenn Block, Filip Wojcieszyn, Justin Rusbatch + https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md + http://scriptcs.net + http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 + ScriptCs.Core + ScriptCs.Core is the core framework assembly for scriptcs. + roslyn csx scriptcs - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - + - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/ScriptCs.Core/ScriptEnvironment.cs b/src/ScriptCs.Core/ScriptEnvironment.cs index 7abe1e66..2e307fc2 100644 --- a/src/ScriptCs.Core/ScriptEnvironment.cs +++ b/src/ScriptCs.Core/ScriptEnvironment.cs @@ -1,15 +1,57 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using ScriptCs.Contracts; namespace ScriptCs { public class ScriptEnvironment : IScriptEnvironment { - public ScriptEnvironment(string[] scriptArgs) + private readonly IConsole _console; + private readonly Printers _printers; + private readonly IScriptInfo _scriptInfo; + + public ScriptEnvironment(string[] scriptArgs, IConsole console, Printers printers, IScriptInfo scriptInfo = null ) { + _console = console; + _printers = printers; + _scriptInfo = scriptInfo; ScriptArgs = scriptArgs; } public IReadOnlyList ScriptArgs { get; private set; } + + public void AddCustomPrinter(Func printer) + { + _console.WriteLine("Adding custom printer for " + typeof(T).Name); + _printers.AddCustomPrinter(printer); + } + + public void Print(object o) + { + _console.WriteLine(_printers.GetStringFor(o)); + } + + public void Print(T o) + { + _console.WriteLine(_printers.GetStringFor(o)); + } + + public string ScriptPath => _scriptInfo.ScriptPath; + + public string[] LoadedScripts => _scriptInfo.LoadedScripts.ToArray(); + + public Assembly ScriptAssembly { get; private set; } + + private bool _initialized; + public void Initialize() + { + if (!_initialized) + { + ScriptAssembly = Assembly.GetCallingAssembly(); + _initialized = true; + } + } } -} \ No newline at end of file +} diff --git a/src/ScriptCs.Core/ScriptExecutor.cs b/src/ScriptCs.Core/ScriptExecutor.cs index c72a26a7..499f0e8b 100644 --- a/src/ScriptCs.Core/ScriptExecutor.cs +++ b/src/ScriptCs.Core/ScriptExecutor.cs @@ -4,22 +4,24 @@ using System.IO; using System.Linq; using System.Reflection; -using ScriptCs.Logging; using ScriptCs.Contracts; namespace ScriptCs { public class ScriptExecutor : IScriptExecutor { + private readonly ILog _log; + public static readonly string[] DefaultReferences = { - "System", - "System.Core", - "System.Data", - "System.Data.DataSetExtensions", - "System.Xml", - "System.Xml.Linq", + "System", + "System.Core", + "System.Data", + "System.Data.DataSetExtensions", + "System.Xml", + "System.Xml.Linq", "System.Net.Http", + "Microsoft.CSharp", typeof(ScriptExecutor).Assembly.Location, typeof(IScriptEnvironment).Assembly.Location }; @@ -28,11 +30,12 @@ public class ScriptExecutor : IScriptExecutor { "System", "System.Collections.Generic", - "System.Linq", - "System.Text", + "System.Linq", + "System.Text", "System.Threading.Tasks", "System.IO", - "System.Net.Http" + "System.Net.Http", + "System.Dynamic" }; private const string ScriptLibrariesInjected = "ScriptLibrariesInjected"; @@ -43,19 +46,19 @@ public class ScriptExecutor : IScriptExecutor public IScriptEngine ScriptEngine { get; private set; } - public ILog Logger { get; private set; } - public AssemblyReferences References { get; private set; } - public ICollection Namespaces { get; private set; } + public IReadOnlyCollection Namespaces { get; private set; } public ScriptPackSession ScriptPackSession { get; protected set; } public IScriptLibraryComposer ScriptLibraryComposer { get; protected set; } + public IScriptInfo ScriptInfo { get; protected set; } + public ScriptExecutor( - IFileSystem fileSystem, IFilePreProcessor filePreProcessor, IScriptEngine scriptEngine, ILog logger) - : this(fileSystem, filePreProcessor, scriptEngine, logger, new NullScriptLibraryComposer()) + IFileSystem fileSystem, IFilePreProcessor filePreProcessor, IScriptEngine scriptEngine, ILogProvider logProvider, IScriptInfo scriptInfo) + : this(fileSystem, filePreProcessor, scriptEngine, logProvider, new NullScriptLibraryComposer(), scriptInfo) { } @@ -63,45 +66,39 @@ public ScriptExecutor( IFileSystem fileSystem, IFilePreProcessor filePreProcessor, IScriptEngine scriptEngine, - ILog logger, - IScriptLibraryComposer composer) + ILogProvider logProvider, + IScriptLibraryComposer composer, + IScriptInfo scriptInfo) { Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgumentProperty("fileSystem", "BinFolder", fileSystem.BinFolder); Guard.AgainstNullArgumentProperty("fileSystem", "DllCacheFolder", fileSystem.DllCacheFolder); Guard.AgainstNullArgument("filePreProcessor", filePreProcessor); Guard.AgainstNullArgument("scriptEngine", scriptEngine); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); Guard.AgainstNullArgument("composer", composer); + Guard.AgainstNullArgument("scriptInfo", scriptInfo); - References = new AssemblyReferences(DefaultReferences); - Namespaces = new Collection(); - ImportNamespaces(DefaultNamespaces); + References = new AssemblyReferences(new[] { GetAssemblyFromName("System.Runtime") }, DefaultReferences); + Namespaces = new ReadOnlyCollection(DefaultNamespaces); FileSystem = fileSystem; FilePreProcessor = filePreProcessor; ScriptEngine = scriptEngine; - Logger = logger; + _log = logProvider.ForCurrentType(); ScriptLibraryComposer = composer; + ScriptInfo = scriptInfo; } - public void ImportNamespaces(params string[] namespaces) + public virtual void ImportNamespaces(params string[] namespaces) { Guard.AgainstNullArgument("namespaces", namespaces); - - foreach (var @namespace in namespaces) - { - Namespaces.Add(@namespace); - } + Namespaces = new ReadOnlyCollection(Namespaces.Union(namespaces).ToArray()); } - public void RemoveNamespaces(params string[] namespaces) + public virtual void RemoveNamespaces(params string[] namespaces) { Guard.AgainstNullArgument("namespaces", namespaces); - - foreach (var @namespace in namespaces) - { - Namespaces.Remove(@namespace); - } + Namespaces = new ReadOnlyCollection(Namespaces.Except(namespaces).ToArray()); } public virtual void AddReferences(params Assembly[] assemblies) @@ -142,7 +139,7 @@ public virtual void Initialize( ScriptEngine.BaseDirectory = bin; ScriptEngine.CacheDirectory = cache; - Logger.Debug("Initializing script packs"); + _log.Debug("Initializing script packs"); var scriptPackSession = new ScriptPackSession(scriptPacks, scriptArgs); ScriptPackSession = scriptPackSession; scriptPackSession.InitializePacks(); @@ -151,15 +148,13 @@ public virtual void Initialize( public virtual void Reset() { References = new AssemblyReferences(DefaultReferences); - Namespaces.Clear(); - ImportNamespaces(DefaultNamespaces); - + Namespaces = new ReadOnlyCollection(DefaultNamespaces); ScriptPackSession.State.Clear(); } public virtual void Terminate() { - Logger.Debug("Terminating packs"); + _log.Debug("Terminating packs"); ScriptPackSession.TerminatePacks(); } @@ -167,7 +162,10 @@ public virtual ScriptResult Execute(string script, params string[] scriptArgs) { var path = Path.IsPathRooted(script) ? script : Path.Combine(FileSystem.CurrentDirectory, script); var result = FilePreProcessor.ProcessFile(path); + ScriptEngine.FileName = Path.GetFileName(path); + ScriptInfo.ScriptPath = Path.GetFullPath(path); + return EngineExecute(Path.GetDirectoryName(path), scriptArgs, result); } @@ -178,15 +176,20 @@ public virtual ScriptResult ExecuteScript(string script, params string[] scriptA } protected internal virtual ScriptResult EngineExecute( - string workingDirectory, - string[] scriptArgs, + string workingDirectory, + string[] scriptArgs, FilePreProcessorResult result ) { InjectScriptLibraries(workingDirectory, result, ScriptPackSession.State); var namespaces = Namespaces.Union(result.Namespaces); var references = References.Union(result.References); - Logger.Debug("Starting execution in engine"); + ScriptInfo.ScriptPath = result.ScriptPath; + foreach (var loadedScript in result.LoadedScripts) + { + ScriptInfo.LoadedScripts.Add(loadedScript); + } + _log.Debug("Starting execution in engine"); return ScriptEngine.Execute(result.Code, scriptArgs, references, namespaces, ScriptPackSession); } @@ -206,12 +209,24 @@ IDictionary state var scriptLibrariesPreProcessorResult = LoadScriptLibraries(workingDirectory); + // script libraries should be injected just before the #line directive + // if there is no #line directive, they can be injected at the beginning of code + if (result.Code == null) result.Code = string.Empty; + var safeInsertIndex = result.Code.IndexOf("#line"); + if (safeInsertIndex < 0) safeInsertIndex = 0; + if (scriptLibrariesPreProcessorResult != null) { - result.Code = scriptLibrariesPreProcessorResult.Code + Environment.NewLine + result.Code; + result.Code = result.Code.Insert(safeInsertIndex, scriptLibrariesPreProcessorResult.Code + Environment.NewLine + + "Env.Initialize();" + Environment.NewLine); result.References.AddRange(scriptLibrariesPreProcessorResult.References); result.Namespaces.AddRange(scriptLibrariesPreProcessorResult.Namespaces); } + else + { + result.Code = result.Code.Insert(safeInsertIndex, "Env.Initialize();" + Environment.NewLine); + } + state.Add(ScriptLibrariesInjected, null); } @@ -227,11 +242,23 @@ protected internal virtual FilePreProcessorResult LoadScriptLibraries(string wor if (FileSystem.FileExists(scriptLibrariesPath)) { - Logger.DebugFormat("Found Script Library at {0}", scriptLibrariesPath); + _log.DebugFormat("Found Script Library at {0}", scriptLibrariesPath); return FilePreProcessor.ProcessFile(scriptLibrariesPath); } return null; } + + private static Assembly GetAssemblyFromName(string assemblyName) + { + try + { + return Assembly.Load(new AssemblyName(assemblyName)); + } + catch + { + return null; + } + } } -} +} \ No newline at end of file diff --git a/src/ScriptCs.Core/ScriptHost.cs b/src/ScriptCs.Core/ScriptHost.cs index 0ff45361..12021bde 100644 --- a/src/ScriptCs.Core/ScriptHost.cs +++ b/src/ScriptCs.Core/ScriptHost.cs @@ -1,4 +1,5 @@ -using ScriptCs.Contracts; +using System; +using ScriptCs.Contracts; namespace ScriptCs { diff --git a/src/ScriptCs.Core/ScriptHostFactory.cs b/src/ScriptCs.Core/ScriptHostFactory.cs index 473aaeff..3fe65ba2 100644 --- a/src/ScriptCs.Core/ScriptHostFactory.cs +++ b/src/ScriptCs.Core/ScriptHostFactory.cs @@ -1,12 +1,24 @@ -using ScriptCs.Contracts; +using System.Linq; +using ScriptCs.Contracts; namespace ScriptCs { public class ScriptHostFactory : IScriptHostFactory { + private readonly IConsole _console; + private readonly Printers _printers; + private IScriptInfo _scriptInfo; + + public ScriptHostFactory(IConsole console, Printers printers, IScriptInfo scriptInfo) + { + _console = console; + _printers = printers; + _scriptInfo = scriptInfo; + } + public IScriptHost CreateScriptHost(IScriptPackManager scriptPackManager, string[] scriptArgs) { - return new ScriptHost(scriptPackManager, new ScriptEnvironment(scriptArgs)); + return new ScriptHost(scriptPackManager, new ScriptEnvironment(scriptArgs, _console, _printers, _scriptInfo)); } } } \ No newline at end of file diff --git a/src/ScriptCs.Core/ScriptInfo.cs b/src/ScriptCs.Core/ScriptInfo.cs new file mode 100644 index 00000000..0aaa7ef0 --- /dev/null +++ b/src/ScriptCs.Core/ScriptInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using ScriptCs.Contracts; + +namespace ScriptCs +{ + public class ScriptInfo : IScriptInfo + { + public ScriptInfo() + { + LoadedScripts = new List(); + } + + public string ScriptPath { get; set; } + public IList LoadedScripts { get; private set; } + } +} diff --git a/src/ScriptCs.Core/ScriptLibraryComposer.cs b/src/ScriptCs.Core/ScriptLibraryComposer.cs index bd84bdf0..c04d0c6b 100644 --- a/src/ScriptCs.Core/ScriptLibraryComposer.cs +++ b/src/ScriptCs.Core/ScriptLibraryComposer.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Text; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -16,19 +15,19 @@ public class ScriptLibraryComposer : IScriptLibraryComposer private readonly IPackageAssemblyResolver _packageAssemblyResolver; private readonly ILog _logger; - public ScriptLibraryComposer(IFileSystem fileSystem, IFilePreProcessor preProcessor, IPackageContainer packageContainer, IPackageAssemblyResolver packageAssemblyResolver, ILog logger) + public ScriptLibraryComposer(IFileSystem fileSystem, IFilePreProcessor preProcessor, IPackageContainer packageContainer, IPackageAssemblyResolver packageAssemblyResolver, ILogProvider logProvider) { Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgument("preProcessor", preProcessor); Guard.AgainstNullArgument("packageContainer", packageContainer); Guard.AgainstNullArgument("packageAssemblyResolver", packageAssemblyResolver); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); _fileSystem = fileSystem; _preProcessor = preProcessor; _packageContainer = packageContainer; _packageAssemblyResolver = packageAssemblyResolver; - _logger = logger; + _logger = logProvider.ForCurrentType(); } internal string GetMainScript(IPackageObject package) @@ -43,7 +42,7 @@ internal string GetMainScript(IPackageObject package) { script = content[0]; } - else if (content.Count() > 1) + else if (content.Length > 1) { _logger.WarnFormat("Script Libraries in '{0}' ignored due to multiple Main files being present", package.FullName); return null; @@ -57,10 +56,7 @@ internal string GetMainScript(IPackageObject package) return script; } - public virtual string ScriptLibrariesFile - { - get { return "ScriptLibraries.csx"; } - } + public virtual string ScriptLibrariesFile => "ScriptLibraries.csx"; public void Compose(string workingDirectory, StringBuilder builder = null) { diff --git a/src/ScriptCs.Core/ScriptLibraryWrapper.cs b/src/ScriptCs.Core/ScriptLibraryWrapper.cs index 73f9a8bd..88d71c23 100644 --- a/src/ScriptCs.Core/ScriptLibraryWrapper.cs +++ b/src/ScriptCs.Core/ScriptLibraryWrapper.cs @@ -11,13 +11,7 @@ public abstract class ScriptLibraryWrapper { private static IScriptHost _scriptHost; - internal static IScriptHost ScriptHost - { - get - { - return _scriptHost; - } - } + internal static IScriptHost ScriptHost => _scriptHost; public static void SetHost(IScriptHost scriptHost) { @@ -29,9 +23,6 @@ public static T Require() where T:IScriptPackContext return _scriptHost.Require(); } - public static IScriptEnvironment Env - { - get { return _scriptHost.Env; } - } + public static IScriptEnvironment Env => _scriptHost.Env; } } diff --git a/src/ScriptCs.Core/ScriptPackManager.cs b/src/ScriptCs.Core/ScriptPackManager.cs index c2269004..cd21946a 100644 --- a/src/ScriptCs.Core/ScriptPackManager.cs +++ b/src/ScriptCs.Core/ScriptPackManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using ScriptCs.Contracts; +using ScriptCs.Exceptions; namespace ScriptCs { @@ -20,7 +21,13 @@ public ScriptPackManager(IEnumerable contexts) public TContext Get() where TContext : IScriptPackContext { - return (TContext)_contexts[typeof(TContext)]; + var key = typeof(TContext); + if (!_contexts.ContainsKey(key)) + { + throw new ScriptPackException(string.Format("Tried to resolve a script pack '{0}', but such script pack is not available in the current execution context.", key)); + } + + return (TContext)_contexts[key]; } } } \ No newline at end of file diff --git a/src/ScriptCs.Core/ScriptPackResolver.cs b/src/ScriptCs.Core/ScriptPackResolver.cs index 59747df5..00d6da11 100644 --- a/src/ScriptCs.Core/ScriptPackResolver.cs +++ b/src/ScriptCs.Core/ScriptPackResolver.cs @@ -12,9 +12,6 @@ public ScriptPackResolver(IEnumerable scriptPacks) _scriptPacks = scriptPacks; } - public IEnumerable GetPacks() - { - return _scriptPacks; - } + public IEnumerable GetPacks() => _scriptPacks; } } \ No newline at end of file diff --git a/src/ScriptCs.Core/ScriptServices.cs b/src/ScriptCs.Core/ScriptServices.cs index 540d56c7..bdd221e9 100644 --- a/src/ScriptCs.Core/ScriptServices.cs +++ b/src/ScriptCs.Core/ScriptServices.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs { @@ -16,10 +16,9 @@ public ScriptServices( IScriptPackResolver scriptPackResolver, IPackageInstaller packageInstaller, IObjectSerializer objectSerializer, - ILog logger, + ILogProvider logProvider, IAssemblyResolver assemblyResolver, IEnumerable replCommands, - IFileSystemMigrator fileSystemMigrator, IConsole console = null, IInstallationProvider installationProvider = null, IScriptLibraryComposer scriptLibraryComposer = null @@ -34,12 +33,11 @@ public ScriptServices( ScriptPackResolver = scriptPackResolver; PackageInstaller = packageInstaller; ObjectSerializer = objectSerializer; - Logger = logger; + LogProvider = logProvider; Console = console; AssemblyResolver = assemblyResolver; InstallationProvider = installationProvider; ReplCommands = replCommands; - FileSystemMigrator = fileSystemMigrator; ScriptLibraryComposer = scriptLibraryComposer; } @@ -50,14 +48,13 @@ public ScriptServices( public IScriptPackResolver ScriptPackResolver { get; private set; } public IPackageInstaller PackageInstaller { get; private set; } public IObjectSerializer ObjectSerializer { get; private set; } - public ILog Logger { get; private set; } + public ILogProvider LogProvider { get; private set; } public IScriptEngine Engine { get; private set; } public IFilePreProcessor FilePreProcessor { get; private set; } public IConsole Console { get; private set; } public IAssemblyResolver AssemblyResolver { get; private set; } public IInstallationProvider InstallationProvider { get; private set; } public IEnumerable ReplCommands { get; private set; } - public IFileSystemMigrator FileSystemMigrator { get; private set; } public IScriptLibraryComposer ScriptLibraryComposer { get; private set; } } } diff --git a/src/ScriptCs.Core/ShebangLineProcessor.cs b/src/ScriptCs.Core/ShebangLineProcessor.cs index d1ef83a5..a422f26b 100644 --- a/src/ScriptCs.Core/ShebangLineProcessor.cs +++ b/src/ScriptCs.Core/ShebangLineProcessor.cs @@ -8,14 +8,8 @@ public interface IShebangLineProcessor : ILineProcessor public class ShebangLineProcessor : DirectiveLineProcessor, IShebangLineProcessor { - protected override string DirectiveName - { - get { return "!/usr/bin/env"; } - } + protected override string DirectiveName => "!/usr/bin/env"; - protected override bool ProcessLine(IFileParser parser, FileParserContext context, string line) - { - return true; - } + protected override bool ProcessLine(IFileParser parser, FileParserContext context, string line) => true; } } \ No newline at end of file diff --git a/src/ScriptCs.Core/StringExtensions.cs b/src/ScriptCs.Core/StringExtensions.cs index d87c90ac..07341764 100644 --- a/src/ScriptCs.Core/StringExtensions.cs +++ b/src/ScriptCs.Core/StringExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Text; namespace ScriptCs { @@ -8,6 +10,107 @@ public static string DefineTrace(this string code) { return string.Format("#define TRACE{0}{1}", Environment.NewLine, code); } + + /// + /// Split string on whitespace, but keeps string with quotes together + /// For example: :cd "\\Foo Bar" + /// :cd + /// "\\Foo Bar". + /// + /// String with or without quotes. + /// Array of strings. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", + Justification = "Guard Against Null Argument method is not need because of the if statement.", + MessageId = "0")] + public static string[] SplitQuoted(this string argument) + { + if (string.IsNullOrWhiteSpace(argument)) + { + return argument.Split(' '); + } + + // count the number of quotes and throw something is not even + // the fastest way is to just loop thru the string + // http://cc.davelozinski.com/c-sharp/fastest-way-to-check-if-a-string-occurs-within-a-string + Func quoteCounterFunc = delegate (string line) + { + int count = 0; + for (int x = 0; x < line.Length; x++) + { + if (line[x] == '"') + { + count++; + } + } + return count; + }; + int quotes = quoteCounterFunc(argument); + if ((quotes % 2) != 0) + { + throw new ArgumentException("String is missing a closing quote"); + } + + List list = new List(argument.Split(' ')); + + // quoted string needs to be combine back together + if (quotes > 0 && list.Count > 0) + { + Predicate findQuoteFunc = delegate (string s) { return s.Contains("\""); }; + // create function to find string item with odd number of quotes + Func findOddQuotedItemFunc = delegate (int startingIndex) { + if (startingIndex < list.Count) + { + do + { + int quickFind = list.FindIndex(startingIndex, findQuoteFunc); + int quickCount = quoteCounterFunc(list[quickFind]); + if ((quickCount % 2) != 0) + { + return quickFind; + } + // we didn't find the quoted line we are looking for + startingIndex = quickFind + 1; + } while (startingIndex < list.Count); + } + return -1; + }; + + int index = 0; + do + { + int start = findOddQuotedItemFunc(index); + if (start > 0) + { + // we have to locate the next string with odd number of quotes + int end = findOddQuotedItemFunc(start + 1); + + string combined = string.Empty; + for (int x = start; x <= end; x++) + { + // because we split on whitespace, we have to put it back when combining + combined += list[x] + ' '; + } + list[start] = combined.TrimEnd(); // remove the extra whitespace that was added + + // removed the other parts of the combined string from the list + do + { + list.RemoveAt(end--); // from the bottom up + } while (start < end); + + // advance to next item in the adjusted list + index = start + 1; + } + else + { + break; + } + + } while (index < list.Count); + } + + return list.ToArray(); + } public static string UndefineTrace(this string code) { diff --git a/src/ScriptCs.Core/UsingLineProcessor.cs b/src/ScriptCs.Core/UsingLineProcessor.cs index 72f695bc..5acbad12 100644 --- a/src/ScriptCs.Core/UsingLineProcessor.cs +++ b/src/ScriptCs.Core/UsingLineProcessor.cs @@ -19,6 +19,12 @@ public bool ProcessLine(IFileParser parser, FileParserContext context, string li return false; } + // for using static, we will not extract the import into the context, but rather let it be treated as code + if (line.Contains(" static ")) + { + return false; + } + var @namespace = GetNamespace(line); if (!context.Namespaces.Contains(@namespace)) { @@ -28,10 +34,7 @@ public bool ProcessLine(IFileParser parser, FileParserContext context, string li return true; } - private static bool IsUsingLine(string line) - { - return line.Trim(' ').StartsWith(UsingString) && !line.Contains("{") && line.Contains(";") && !line.Contains("="); - } + private static bool IsUsingLine(string line) => line.Trim(' ').StartsWith(UsingString) && !line.Contains("{") && line.Contains(";") && !line.Contains("="); private static string GetNamespace(string line) { diff --git a/src/ScriptCs.Core/packages.config b/src/ScriptCs.Core/packages.config deleted file mode 100644 index 3a1ae66f..00000000 --- a/src/ScriptCs.Core/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/MonoHost.cs b/src/ScriptCs.Engine.Mono/MonoHost.cs deleted file mode 100644 index 71cf34fc..00000000 --- a/src/ScriptCs.Engine.Mono/MonoHost.cs +++ /dev/null @@ -1,33 +0,0 @@ -extern alias MonoCSharp; - -using ScriptCs.Contracts; - -namespace ScriptCs.Engine.Mono -{ - public class MonoHost : IScriptHost - { - private static ScriptHost _scriptHost; - - public static void SetHost(ScriptHost scriptHost) - { - _scriptHost = scriptHost; - } - - public static IScriptEnvironment Env { get { return _scriptHost.Env; } } - - IScriptEnvironment IScriptHost.Env - { - get { return _scriptHost.Env; } - } - - public static T Require() where T : IScriptPackContext - { - return _scriptHost.Require(); - } - - T IScriptHost.Require() - { - return _scriptHost.Require(); - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/MonoModule.cs b/src/ScriptCs.Engine.Mono/MonoModule.cs deleted file mode 100644 index bbb41a30..00000000 --- a/src/ScriptCs.Engine.Mono/MonoModule.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ScriptCs.Contracts; - -namespace ScriptCs.Engine.Mono -{ - [Module("mono")] - public class MonoModule : IModule - { - public void Initialize(IModuleConfiguration config) - { - Guard.AgainstNullArgument("config", config); - - if (!config.Overrides.ContainsKey(typeof(IScriptEngine))) - { - config.ScriptEngine(); - } - } - } -} diff --git a/src/ScriptCs.Engine.Mono/MonoScriptEngine.cs b/src/ScriptCs.Engine.Mono/MonoScriptEngine.cs deleted file mode 100644 index 7a93ca98..00000000 --- a/src/ScriptCs.Engine.Mono/MonoScriptEngine.cs +++ /dev/null @@ -1,163 +0,0 @@ -extern alias MonoCSharp; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Mono.Collections.Generic; -using MonoCSharp::Mono.CSharp; -using ScriptCs.Contracts; -using ScriptCs.Engine.Mono.Segmenter; -using ScriptCs.Logging; - -namespace ScriptCs.Engine.Mono -{ - public class MonoScriptEngine : IReplEngine - { - public const string SessionKey = "MonoSession"; - - private readonly IScriptHostFactory _scriptHostFactory; - - public string BaseDirectory { get; set; } - public string CacheDirectory { get; set; } - public string FileName { get; set; } - - public MonoScriptEngine(IScriptHostFactory scriptHostFactory, ILog logger) - { - _scriptHostFactory = scriptHostFactory; - Logger = logger; - } - - public ILog Logger { get; set; } - - public ICollection GetLocalVariables(ScriptPackSession scriptPackSession) - { - if (scriptPackSession != null && scriptPackSession.State.ContainsKey(SessionKey)) - { - var sessionState = (SessionState)scriptPackSession.State[SessionKey]; - var vars = sessionState.Session.GetVars(); - if (!string.IsNullOrWhiteSpace(vars) && vars.Contains(Environment.NewLine)) - { - return vars.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - } - } - - return new Collection(); - } - - public ScriptResult Execute( - string code, - string[] scriptArgs, - AssemblyReferences references, - IEnumerable namespaces, - ScriptPackSession scriptPackSession) - { - Guard.AgainstNullArgument("references", references); - Guard.AgainstNullArgument("scriptPackSession", scriptPackSession); - - references = references.Union(scriptPackSession.References); - - SessionState sessionState; - var isFirstExecution = !scriptPackSession.State.ContainsKey(SessionKey); - - if (isFirstExecution) - { - code = code.DefineTrace(); - Logger.Debug("Creating session"); - var context = new CompilerContext( - new CompilerSettings { AssemblyReferences = references.Paths.ToList() }, - new ConsoleReportPrinter()); - - var evaluator = new Evaluator(context); - var allNamespaces = namespaces.Union(scriptPackSession.Namespaces).Distinct(); - - var host = _scriptHostFactory.CreateScriptHost( - new ScriptPackManager(scriptPackSession.Contexts), scriptArgs); - - MonoHost.SetHost((ScriptHost)host); - ScriptLibraryWrapper.SetHost(host); - - evaluator.ReferenceAssembly(typeof(MonoHost).Assembly); - evaluator.InteractiveBaseClass = typeof(MonoHost); - - sessionState = new SessionState - { - References = references, - Namespaces = new HashSet(), - Session = evaluator, - }; - - ImportNamespaces(allNamespaces, sessionState); - scriptPackSession.State[SessionKey] = sessionState; - } - else - { - Logger.Debug("Reusing existing session"); - sessionState = (SessionState)scriptPackSession.State[SessionKey]; - - var newReferences = sessionState.References == null - ? references - : references.Except(sessionState.References); - - foreach (var reference in newReferences.Paths) - { - Logger.DebugFormat("Adding reference to {0}", reference); - sessionState.Session.LoadAssembly(reference); - } - - sessionState.References = references; - - var newNamespaces = sessionState.Namespaces == null - ? namespaces - : namespaces.Except(sessionState.Namespaces); - - ImportNamespaces(newNamespaces, sessionState); - } - - Logger.Debug("Starting execution"); - var result = Execute(code, sessionState.Session); - Logger.Debug("Finished execution"); - - return result; - } - - protected virtual ScriptResult Execute(string code, Evaluator session) - { - Guard.AgainstNullArgument("session", session); - - try - { - object scriptResult = null; - var segmenter = new ScriptSegmenter(); - foreach (var segment in segmenter.Segment(code)) - { - bool resultSet; - session.Evaluate(segment.Code, out scriptResult, out resultSet); - } - - return new ScriptResult(returnValue: scriptResult); - } - catch (AggregateException ex) - { - return new ScriptResult(executionException: ex.InnerException); - } - catch (Exception ex) - { - return new ScriptResult(executionException: ex); - } - } - - private void ImportNamespaces(IEnumerable namespaces, SessionState sessionState) - { - var builder = new StringBuilder(); - foreach (var ns in namespaces) - { - Logger.DebugFormat(ns); - builder.AppendLine(string.Format("using {0};", ns)); - sessionState.Namespaces.Add(ns); - } - - sessionState.Session.Compile(builder.ToString()); - } - } -} diff --git a/src/ScriptCs.Engine.Mono/Properties/AssemblyInfo.cs b/src/ScriptCs.Engine.Mono/Properties/AssemblyInfo.cs deleted file mode 100644 index 65e7f1a6..00000000 --- a/src/ScriptCs.Engine.Mono/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("ScriptCs.Engine.Mono")] -[assembly: AssemblyDescription("ScriptCs.Engine.Mono provides a Mono-based script engine for scriptcs.")] diff --git a/src/ScriptCs.Engine.Mono/Properties/ScriptCs.Engine.Mono.nuspec b/src/ScriptCs.Engine.Mono/Properties/ScriptCs.Engine.Mono.nuspec deleted file mode 100644 index 51e6c689..00000000 --- a/src/ScriptCs.Engine.Mono/Properties/ScriptCs.Engine.Mono.nuspec +++ /dev/null @@ -1,20 +0,0 @@ - - - - ScriptCs.Engine.Mono - $version$ - Glenn Block, Filip Wojcieszyn, Justin Rusbatch, Kristian Hellang, Damian Schenkelman, Adam Ralph - Glenn Block, Justin Rusbatch, Filip Wojcieszyn - https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md - http://scriptcs.net - http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 - ScriptCs.Engine.Mono provides a Mono-based script engine for scriptcs. - mono csx script scriptcs - - - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/ScriptCs.Engine.Mono.csproj b/src/ScriptCs.Engine.Mono/ScriptCs.Engine.Mono.csproj deleted file mode 100644 index 88fd6c2a..00000000 --- a/src/ScriptCs.Engine.Mono/ScriptCs.Engine.Mono.csproj +++ /dev/null @@ -1,124 +0,0 @@ - - - - - Debug - AnyCPU - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B} - Library - ScriptCs.Engine.Mono - ScriptCs.Engine.Mono - Properties - 512 - v4.5 - - ..\..\ - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - - False - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Cecil.dll - - - False - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - - - False - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Xml.dll - - - False - ..\..\packages\Mono.CSharp.4.0.0.143\lib\4.5\Mono.CSharp.dll - MonoCSharp - - - - - - - - - - - ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll - - - ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll - - - ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll - - - ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll - - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - Guard.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/CodeAnalyzer.cs b/src/ScriptCs.Engine.Mono/Segmenter/Analyser/CodeAnalyzer.cs deleted file mode 100644 index ea6a9219..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/CodeAnalyzer.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Linq; - -using ScriptCs.Engine.Mono.Segmenter.Analyser.Visitors; - -using ICSharpCode.NRefactory.CSharp; - -namespace ScriptCs.Engine.Mono.Segmenter.Analyser -{ - public class CodeAnalyzer - { - public bool IsClass(string code) - { - var visitor = new ClassTypeVisitor(); - var parser = new CSharpParser(); - var syntaxTree = parser.Parse(code); - syntaxTree.AcceptVisitor(visitor); - syntaxTree.Freeze(); - - return visitor.GetClassDeclarations().Any(); - } - - public bool IsMethod(string code) - { - Guard.AgainstNullArgument("code", code); - - var @class = "class A { " + code + " } "; - var visitor = new MethodVisitor(); - var parser = new CSharpParser(); - var syntaxTree = parser.Parse(@class); - syntaxTree.AcceptVisitor(visitor); - syntaxTree.Freeze(); - - return visitor.GetMethodDeclarations().Any() && code.TrimEnd().EndsWith("}"); - } - - public MethodResult ExtractPrototypeAndMethod(string code) - { - Guard.AgainstNullArgument("code", code); - - var @class = "class A { " + code + " } "; - var visitor = new MethodVisitor(); - var parser = new CSharpParser(); - var syntaxTree = parser.Parse(@class); - syntaxTree.AcceptVisitor(visitor); - syntaxTree.Freeze(); - - var result = visitor.GetMethodDeclarations().FirstOrDefault(); - - // find newlines in method signature to maintain linenumbers - var newLines = code.Substring(0, code.IndexOf("{", StringComparison.Ordinal) - 1) - .Where(x => x.Equals('\n')) - .Aggregate(string.Empty, (a, c) => a + c); - - // use code methodblock to maintain linenumbers - var codeBlock = code.Substring(code.IndexOf("{", StringComparison.Ordinal), code.LastIndexOf("}", StringComparison.Ordinal) - code.IndexOf("{", StringComparison.Ordinal) + 1); - var method = result.MethodExpression.ToString(); - var blockStart = method.IndexOf("{", StringComparison.Ordinal); - var blockEnd = method.LastIndexOf("}", StringComparison.Ordinal); - method = method.Remove(blockStart, blockEnd - blockStart + 1); - method = method.Insert(blockStart, codeBlock); - - return new MethodResult - { - ProtoType = result.MethodPrototype.ToString().Trim() + newLines, - MethodExpression = newLines + method.Trim() - }; - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/MethodResult.cs b/src/ScriptCs.Engine.Mono/Segmenter/Analyser/MethodResult.cs deleted file mode 100644 index 3d46069a..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/MethodResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter.Analyser -{ - public class MethodResult - { - public string ProtoType { get; set; } - - public string MethodExpression { get; set; } - } -} diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/ClassTypeVisitor.cs b/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/ClassTypeVisitor.cs deleted file mode 100644 index 38bdda2d..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/ClassTypeVisitor.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; - -using ICSharpCode.NRefactory.CSharp; - -namespace ScriptCs.Engine.Mono.Segmenter.Analyser.Visitors -{ - internal class ClassTypeVisitor : DepthFirstAstVisitor - { - private readonly List _classes; - - internal ClassTypeVisitor() - { - _classes = new List(); - } - - internal IList GetClassDeclarations() - { - return _classes; - } - - public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) - { - _classes.Add(typeDeclaration); - base.VisitTypeDeclaration(typeDeclaration); - } - } -} diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitor.cs b/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitor.cs deleted file mode 100644 index bbf5fcc4..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitor.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using ICSharpCode.NRefactory.CSharp; - -namespace ScriptCs.Engine.Mono.Segmenter.Analyser.Visitors -{ - internal class MethodVisitor : DepthFirstAstVisitor - { - private readonly List _methods; - - internal MethodVisitor() - { - _methods = new List(); - } - - internal IList GetMethodDeclarations() - { - return _methods; - } - - public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) - { - Guard.AgainstNullArgument("methodDeclaration", methodDeclaration); - - IEnumerable parameters = methodDeclaration - .GetChildrenByRole(Roles.Parameter) - .Select(x => (ParameterDeclaration)x.Clone()); - - var isVoid = false; - var isAsync = methodDeclaration.Modifiers.HasFlag(Modifiers.Async); - AstType returnType = methodDeclaration.GetChildByRole(Roles.Type).Clone(); - var type = returnType as PrimitiveType; - if (type != null) - { - isVoid = string.Compare( - type.Keyword, "void", StringComparison.OrdinalIgnoreCase) == 0; - } - - var methodType = new SimpleType(Identifier.Create(isVoid ? "Action" : "Func")); - - IEnumerable types = parameters.Select( - x => x.GetChildByRole(Roles.Type).Clone()); - - methodType - .TypeArguments - .AddRange(types); - - if (!isVoid) - { - methodType.TypeArguments.Add(returnType); - } - var methodName = GetIdentifierName(methodDeclaration); - - var methodBody = methodDeclaration - .GetChildrenByRole(Roles.Body) - .FirstOrDefault(); - if (methodBody == null) - { - // method has no method body - return; - } - methodBody = (BlockStatement)methodBody.Clone(); - - var prototype = new VariableDeclarationStatement { Type = methodType }; - prototype.Variables.Add(new VariableInitializer(methodName)); - - var anonymousMethod = new AnonymousMethodExpression(methodBody, parameters) { IsAsync = isAsync }; - var expression = new ExpressionStatement - { - Expression = new AssignmentExpression( - new IdentifierExpression(methodName), - anonymousMethod) - }; - - _methods.Add(new MethodVisitorResult - { - MethodDefinition = methodDeclaration, - MethodPrototype = prototype, - MethodExpression = expression - }); - } - - private static string GetIdentifierName(AstNode node) - { - foreach (var obj in - from child in node.GetChildrenByRole(Roles.Identifier) - from propertyInfo in child - .GetType() - .GetProperties( - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public) - .Where(x => x.Name == "Name") - select propertyInfo.GetValue(child, null)) - { - return obj.ToString(); - } - throw new MissingFieldException("Missing Role 'Identifier' from AstNode"); - } - } -} diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitorResult.cs b/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitorResult.cs deleted file mode 100644 index fd1bbb66..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Analyser/Visitors/MethodVisitorResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; - -namespace ScriptCs.Engine.Mono.Segmenter.Analyser.Visitors -{ - public class MethodVisitorResult - { - public MethodDeclaration MethodDefinition { get; set; } - - public VariableDeclarationStatement MethodPrototype { get; set; } - - public ExpressionStatement MethodExpression { get; set; } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/LexerResult.cs b/src/ScriptCs.Engine.Mono/Segmenter/Lexer/LexerResult.cs deleted file mode 100644 index 6213f6ea..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/LexerResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter.Lexer -{ - public class LexerResult - { - public int Token { get; set; } - public string TokenValue { get; set; } - public int Start { get; set; } - public int End { get; set; } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/ScriptLexer.cs b/src/ScriptCs.Engine.Mono/Segmenter/Lexer/ScriptLexer.cs deleted file mode 100644 index a2bc3ffe..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/ScriptLexer.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.IO; -using System.Text.RegularExpressions; - -namespace ScriptCs.Engine.Mono.Segmenter.Lexer -{ - public sealed class ScriptLexer : IDisposable - { - private int _lastChar = ' '; - private int _position; - private readonly StringReader _sr; - - public ScriptLexer(string code) - { - _position = -1; //inital pos - - _sr = new StringReader(code); - } - - /// - /// Return the next token from script. - /// - /// - /// The next token. - /// - public LexerResult GetToken() - { - // skip any whitespace - while (IsSpace((char)_lastChar)) - { - _lastChar = Read(); - } - - if (_lastChar == Token.Quote) - { - var @string = string.Empty; - @string += (char)_lastChar; - - int previous; - do - { - previous = _lastChar; - _lastChar = Read(); - @string += (char)_lastChar; - } while (!(_lastChar == Token.Quote && previous != Token.EscapeChar) - && _lastChar != Token.Eof); - - _lastChar = Read(); //eat - - return new LexerResult - { - Token = Token.String, - TokenValue = @string, - Start = _position - @string.Length, - End = _position - }; - } - - if (_lastChar == Token.SingleQuote) - { - string @char = string.Empty; - @char += (char)_lastChar; - - _lastChar = Read(); - int count = 0; - while (count < 2 && _lastChar != Token.Eof) - { - count++; - @char += (char)_lastChar; - _lastChar = Read(); - } - - return new LexerResult - { - Token = Token.Character, - TokenValue = @char, - Start = _position - @char.Length, - End = _position - }; - } - - // identifiers [a-zA-Z_][a-zA-Z0-9_] - if (IsAlphaNumeric(_lastChar)) - { - var identifier = string.Empty; - identifier += (char)_lastChar; - _lastChar = Read(); - while (IsAlphaNumeric(_lastChar)) - { - identifier += (char)_lastChar; - _lastChar = Read(); - } - - var token = Token.Identifier; - if (identifier.Equals("do")) - { - token = Token.Do; - } - else if (identifier.Equals("while")) - { - token = Token.While; - } - - return new LexerResult - { - Token = token, - TokenValue = identifier, - Start = _position - identifier.Length, - End = _position - }; - } - - // single line comment - if (_lastChar == Token.ForwardSlash && _sr.Peek() == Token.ForwardSlash) - { - do - { - _lastChar = Read(); - } while (_lastChar != Token.Eof - && _lastChar != Token.NewLine && _lastChar != Token.LineFeed); - - if (_lastChar != Token.Eof) - { - return GetToken(); - } - } - - // multi line comment - if (_lastChar == Token.ForwardSlash && _sr.Peek() == Token.Star) - { - _lastChar = Read(); //eat - _lastChar = Read(); //eat - int nextChar; - do - { - _lastChar = Read(); - nextChar = _sr.Peek(); - } while (_lastChar != Token.Eof - && (_lastChar != Token.Star || nextChar != Token.ForwardSlash)); - - if (_lastChar != Token.Eof) - { - _lastChar = Read(); //eat - _lastChar = Read(); //eat - return GetToken(); - } - } - - if (_lastChar == Token.Eof) - { - return new LexerResult - { - Token = Token.Eof, - TokenValue = string.Empty, - Start = _position - 1, - End = _position - }; - } - - var thisChar = _lastChar; - _lastChar = Read(); - return new LexerResult - { - Token = thisChar, - TokenValue = string.Empty, - Start = _position - 1, - End = _position - }; - } - - private int Read() - { - _position += 1; - return _sr.Read(); - } - - public static bool IsSpace(int token) - { - return token == Token.Space - || token == Token.NewLine - || token == Token.LineFeed - || token == Token.Tab; - } - - public void Dispose() - { - _sr.Dispose(); - } - - private static bool IsAlphaNumeric(int token) - { - var rg = new Regex(@"^[a-zA-Z0-9_]*$"); - return rg.IsMatch(((char)token).ToString()); - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/Token.cs b/src/ScriptCs.Engine.Mono/Segmenter/Lexer/Token.cs deleted file mode 100644 index 409bfc00..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Lexer/Token.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter.Lexer -{ - public static class Token - { - public const int Eof = -1; - public const int Identifier = -2; - public const int String = -3; - public const int Character = -4; - public const int Do = -5; - public const int While = -6; - public const int LeftBracket = '{'; - public const int RightBracket = '}'; - public const int LeftParenthese = '('; - public const int RightParenthese = ')'; - public const int SemiColon = ';'; - public const int ForwardSlash = '/'; - public const int EscapeChar = '\\'; - public const int Star = '*'; - public const int Space = ' '; - public const int Tab = '\t'; - public const int LineFeed = '\r'; - public const int NewLine = '\n'; - public const int Quote = '"'; - public const int SingleQuote = '\''; - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionParser.cs b/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionParser.cs deleted file mode 100644 index 76e2b810..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionParser.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; - -using ScriptCs.Engine.Mono.Segmenter.Lexer; - -namespace ScriptCs.Engine.Mono.Segmenter.Parser -{ - public sealed class RegionParser : IDisposable - { - private ScriptLexer _lexer; - private LexerResult _current; - - public List Parse(string code) - { - _lexer = new ScriptLexer(code); - _current = _lexer.GetToken(); - return GetRegionBlocks(); - } - - public void Dispose() - { - _lexer.Dispose(); - } - - private List GetRegionBlocks() - { - var result = new List(); - while (true) - { - RegionResult region; - switch (_current.Token) - { - case Token.Eof: return result; - case Token.Do: // do-while has two blocks - region = ParseBlock(); - _current = _lexer.GetToken(); - if (_current.Token == Token.While) - { - var whileRegion = ParseBlock(); - var doWhile = region.Combine(whileRegion); - result.Add(doWhile); - _current = _lexer.GetToken(); - break; - } - result.Add(region); - break; - default: - region = ParseBlock(); - result.Add(region); - _current = _lexer.GetToken(); - break; - } - } - } - - private RegionResult ParseBlock() - { - var start = _current.Start; - - // first token is Left curly bracket. - bool block = _current.Token == Token.LeftBracket; - - while (_current.Token != Token.Eof) - { - _current = _lexer.GetToken(); - - if ((!block && _current.Token == Token.SemiColon) - || (block && _current.Token == Token.RightBracket) - || _current.Token == Token.Eof) - { - return new RegionResult - { - Offset = start, - Length = _current.End - start - }; - } - - if (_current.Token == Token.LeftParenthese) - { - var isComplete = SkipScope(Token.LeftParenthese, Token.RightParenthese); - if (_current.Token == Token.Eof) - { - return new RegionResult - { - Offset = start, - Length = _current.End - start, - IsCompleteBlock = isComplete - }; - } - - continue; - } - - if (_current.Token == Token.LeftBracket) - { - bool isComplete = SkipScope(Token.LeftBracket, Token.RightBracket); - return new RegionResult - { - Offset = start, - Length = _current.End - start, - IsCompleteBlock = isComplete - }; - } - } - - throw new InvalidOperationException(string.Format("{0} should never reach this point.", typeof(RegionParser).Name)); - } - - private bool SkipScope(int leftToken, int rightToken) - { - if (_current.Token != leftToken) - { - throw new ArgumentException("Invalid use of SkipBlock method, current token should equal left token parameter"); - } - - var scope = new Stack(); - scope.Push(1); - - while (_current.Token != Token.Eof) - { - _current = _lexer.GetToken(); - - if (_current.Token == leftToken) - { - scope.Push(1); - } - - if (_current.Token == rightToken) - { - scope.Pop(); - } - - if (scope.Count == 0) - { - return true; - } - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionResult.cs b/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionResult.cs deleted file mode 100644 index 0b4f2edf..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/Parser/RegionResult.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter.Parser -{ - public class RegionResult - { - public int Offset { get; set; } - - public int Length { get; set; } - - public bool IsCompleteBlock { get; set; } - - public RegionResult Combine(RegionResult region) - { - Guard.AgainstNullArgument("region", region); - - return new RegionResult - { - Length = Length + region.Length + (region.Offset - (Offset + Length)), - Offset = Offset, - IsCompleteBlock = IsCompleteBlock && region.IsCompleteBlock - }; - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/ScriptSegmenter.cs b/src/ScriptCs.Engine.Mono/Segmenter/ScriptSegmenter.cs deleted file mode 100644 index f3333933..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/ScriptSegmenter.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; - -using ScriptCs.Engine.Mono.Segmenter.Analyser; -using ScriptCs.Engine.Mono.Segmenter.Parser; - -namespace ScriptCs.Engine.Mono.Segmenter -{ - public class ScriptSegmenter - { - public List Segment(string code) - { - const string ScriptPattern = @"#line 1.*?\n"; - var isScriptFile = Regex.IsMatch(code, ScriptPattern); - if (isScriptFile) - { - // Remove debug line - code = Regex.Replace(code, ScriptPattern, ""); - } - - var analyser = new CodeAnalyzer(); - var result = new List(); - using (var parser = new RegionParser()) - { - foreach (var region in parser.Parse(code)) - { - // Calculate region linenumber - var lineNr = code.Substring(0, region.Offset).Count(x => x.Equals('\n')); - - var segment = code.Substring(region.Offset, region.Length); - - if (analyser.IsClass(segment)) - { - result.Add(new SegmentResult - { - Type = SegmentType.Class, - BeginLine = lineNr, - Code = segment - }); - } - else - { - var isMethod = analyser.IsMethod(segment); - - if (isMethod) - { - // method ok - var method = analyser.ExtractPrototypeAndMethod(segment); - - result.Add(new SegmentResult - { - Type = SegmentType.Prototype, - BeginLine = lineNr, - Code = method.ProtoType - }); - - result.Add(new SegmentResult - { - Type = SegmentType.Method, - BeginLine = lineNr, - Code = method.MethodExpression - }); - } - else - { - result.Add(new SegmentResult - { - Type = SegmentType.Evaluation, - BeginLine = lineNr, - Code = segment - }); - } - } - } - } - - return result - .OrderBy(x => x.Type) - .ToList(); - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/SegmentResult.cs b/src/ScriptCs.Engine.Mono/Segmenter/SegmentResult.cs deleted file mode 100644 index 7051ece4..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/SegmentResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter -{ - public class SegmentResult - { - public SegmentType Type { get; set; } - - public int BeginLine { get; set; } - - public string Code { get; set; } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/Segmenter/SegmentType.cs b/src/ScriptCs.Engine.Mono/Segmenter/SegmentType.cs deleted file mode 100644 index 34f6568b..00000000 --- a/src/ScriptCs.Engine.Mono/Segmenter/SegmentType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ScriptCs.Engine.Mono.Segmenter -{ - public enum SegmentType - { - NotSet = 0, - Class = 1, - Prototype = 3, - Method = 4, - Evaluation = 5 - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/app.config b/src/ScriptCs.Engine.Mono/app.config deleted file mode 100644 index 01f4cba0..00000000 --- a/src/ScriptCs.Engine.Mono/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Engine.Mono/packages.config b/src/ScriptCs.Engine.Mono/packages.config deleted file mode 100644 index b86db7de..00000000 --- a/src/ScriptCs.Engine.Mono/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/RoslynScriptPersistentEngine.cs b/src/ScriptCs.Engine.Roslyn/CSharpPersistentEngine.cs similarity index 62% rename from src/ScriptCs.Engine.Roslyn/RoslynScriptPersistentEngine.cs rename to src/ScriptCs.Engine.Roslyn/CSharpPersistentEngine.cs index 856d80b2..380e63a7 100644 --- a/src/ScriptCs.Engine.Roslyn/RoslynScriptPersistentEngine.cs +++ b/src/ScriptCs.Engine.Roslyn/CSharpPersistentEngine.cs @@ -3,18 +3,19 @@ using System.Linq; using System.Reflection; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Engine.Roslyn { - public class RoslynScriptPersistentEngine : RoslynScriptCompilerEngine + public class CSharpPersistentEngine : CSharpScriptCompilerEngine { private readonly IFileSystem _fileSystem; - private const string RoslynAssemblyNameCharacter = "ℛ"; + private readonly ILog _log; - public RoslynScriptPersistentEngine(IScriptHostFactory scriptHostFactory, ILog logger, IFileSystem fileSystem) - : base(scriptHostFactory, logger) + public CSharpPersistentEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider, IFileSystem fileSystem) + : base(scriptHostFactory, logProvider) { + Guard.AgainstNullArgument("logProvider", logProvider); + _log = logProvider.ForCurrentType(); _fileSystem = fileSystem; } @@ -27,7 +28,7 @@ protected override bool ShouldCompile() protected override Assembly LoadAssembly(byte[] exeBytes, byte[] pdbBytes) { - this.Logger.DebugFormat("Writing assembly to {0}.", FileName); + _log.DebugFormat("Writing assembly to {0}.", FileName); if (!_fileSystem.DirectoryExists(CacheDirectory)) { @@ -37,11 +38,8 @@ protected override Assembly LoadAssembly(byte[] exeBytes, byte[] pdbBytes) var dllPath = GetDllTargetPath(); _fileSystem.WriteAllBytes(dllPath, exeBytes); - Logger.DebugFormat("Loading assembly {0}.", dllPath); - - // the assembly is automatically loaded into the AppDomain when compiled - // just need to find and return it - return AppDomain.CurrentDomain.GetAssemblies().LastOrDefault(x => x.FullName.StartsWith(RoslynAssemblyNameCharacter)); + _log.DebugFormat("Loading assembly {0}.", dllPath); + return LoadAssemblyFromCache(); } protected override Assembly LoadAssemblyFromCache() diff --git a/src/ScriptCs.Engine.Roslyn/CSharpReplEngine.cs b/src/ScriptCs.Engine.Roslyn/CSharpReplEngine.cs new file mode 100644 index 00000000..01e841ac --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/CSharpReplEngine.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Scripting; +using ScriptCs.Contracts; + +namespace ScriptCs.Engine.Roslyn +{ + public class CSharpReplEngine : CSharpScriptEngine, IReplEngine + { + public CSharpReplEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider) + : base(scriptHostFactory, logProvider) + { + } + + public ICollection GetLocalVariables(ScriptPackSession scriptPackSession) + { + return this.GetLocalVariables(SessionKey, scriptPackSession); + } + + protected override ScriptResult Execute(string code, object globals, SessionState sessionState) + { + if (string.IsNullOrWhiteSpace(FileName) && !IsCompleteSubmission(code)) + return ScriptResult.Incomplete; + + if (sessionState.Session != null) + { + try + { + Log.Debug("Starting subsequent REPL execution"); + var result = sessionState.Session.ContinueWithAsync(code, ScriptOptions).GetAwaiter().GetResult(); + Log.Debug("Finished subsequent REPL execution"); + sessionState.Session = result; + return new ScriptResult(returnValue: result.ReturnValue); + } + catch (AggregateException ex) + { + return new ScriptResult(executionException: ex.InnerException); + } + catch (CompilationErrorException ex) + { + return new ScriptResult(compilationException: ex); + } + catch (Exception ex) + { + return new ScriptResult(executionException: ex); + } + } + + return base.Execute(code, globals, sessionState); + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/CSharpScriptCompilerEngine.cs b/src/ScriptCs.Engine.Roslyn/CSharpScriptCompilerEngine.cs new file mode 100644 index 00000000..3360ddf5 --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/CSharpScriptCompilerEngine.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using ScriptCs.Contracts; +using ScriptCs.Exceptions; +using Microsoft.CodeAnalysis.Emit; + +namespace ScriptCs.Engine.Roslyn +{ + public abstract class CSharpScriptCompilerEngine : CommonScriptEngine + { + private const string CompiledScriptClass = "Submission#0"; + private const string CompiledScriptMethod = ""; + private readonly ILog _log; + + protected CSharpScriptCompilerEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider) + : base(scriptHostFactory, logProvider) + { + Guard.AgainstNullArgument("logProvider", logProvider); + _log = logProvider.ForCurrentType(); + } + + protected abstract bool ShouldCompile(); + + protected abstract Assembly LoadAssembly(byte[] exeBytes, byte[] pdbBytes); + + protected abstract Assembly LoadAssemblyFromCache(); + + protected override ScriptResult Execute(string code, object globals, SessionState sessionState) + { + return ShouldCompile() + ? CompileAndExecute(code, globals) + : InvokeEntryPointMethod(globals, LoadAssemblyFromCache()); + } + + protected override ScriptState GetScriptState(string code, object globals) + { + return null; + } + + protected ScriptResult CompileAndExecute(string code, object globals) + { + _log.Debug("Compiling submission"); + try + { + var script = CSharpScript.Create(code, ScriptOptions, globals.GetType()); + var compilation = script.GetCompilation(); + + using (var exeStream = new MemoryStream()) + using (var pdbStream = new MemoryStream()) + { + var result = compilation.Emit(exeStream, pdbStream: pdbStream, options: new EmitOptions(). + WithDebugInformationFormat(GetPlatformSpecificDebugInformationFormat())); + + if (result.Success) + { + _log.Debug("Compilation was successful."); + + var assembly = LoadAssembly(exeStream.ToArray(), pdbStream.ToArray()); + return InvokeEntryPointMethod(globals, assembly); + } + + var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x.ToString())); + + _log.ErrorFormat("Error occurred when compiling: {0})", errors); + + return new ScriptResult(compilationException: new ScriptCompilationException(errors)); + } + } + catch (Exception compileException) + { + //we catch Exception rather than CompilationErrorException because there might be issues with assembly loading too + return new ScriptResult(compilationException: new ScriptCompilationException(compileException.Message, compileException)); + } + } + + private ScriptResult InvokeEntryPointMethod(object globals, Assembly assembly) + { + _log.Debug("Retrieving compiled script class (reflection)."); + + // the following line can throw NullReferenceException, if that happens it's useful to notify that an error ocurred + var type = assembly.GetType(CompiledScriptClass); + _log.Debug("Retrieving compiled script method (reflection)."); + var method = type.GetMethod(CompiledScriptMethod, BindingFlags.Static | BindingFlags.Public); + + try + { + _log.Debug("Invoking method."); + var submissionStates = new object[2]; + submissionStates[0] = globals; + var result = method.Invoke(null, new[] {submissionStates}) as Task; + return new ScriptResult(returnValue: result.GetAwaiter().GetResult()); + } + catch (Exception executeException) + { + _log.Error("An error occurred when executing the scripts."); + + var ex = executeException.InnerException ?? executeException; + return new ScriptResult(executionException: ex); + } + } + + private static DebugInformationFormat GetPlatformSpecificDebugInformationFormat() + { + // Mono, use PortablePdb + if (Type.GetType("Mono.Runtime") != null) + { + return DebugInformationFormat.PortablePdb; + } + + // otherwise standard PDB + return DebugInformationFormat.Pdb; + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/CSharpScriptEngine.cs b/src/ScriptCs.Engine.Roslyn/CSharpScriptEngine.cs new file mode 100644 index 00000000..ad99e9b9 --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/CSharpScriptEngine.cs @@ -0,0 +1,35 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using ScriptCs.Contracts; + +namespace ScriptCs.Engine.Roslyn +{ + public class CSharpScriptEngine : CommonScriptEngine + { + public CSharpScriptEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider) : base(scriptHostFactory, logProvider) + { + } + + + protected override ScriptState GetScriptState(string code, object globals) + { + return CSharpScript.RunAsync(code, ScriptOptions, globals).GetAwaiter().GetResult(); + } + + protected bool IsCompleteSubmission(string code) + { + //invalid REPL command + if (code.StartsWith(":")) + { + return true; + } + + var options = new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Parse, SourceCodeKind.Script); + + var syntaxTree = SyntaxFactory.ParseSyntaxTree(code, options: options); + return SyntaxFactory.IsCompleteSubmission(syntaxTree); + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/RoslynScriptInMemoryEngine.cs b/src/ScriptCs.Engine.Roslyn/CSharpScriptInMemoryEngine.cs similarity index 61% rename from src/ScriptCs.Engine.Roslyn/RoslynScriptInMemoryEngine.cs rename to src/ScriptCs.Engine.Roslyn/CSharpScriptInMemoryEngine.cs index 73bb4532..c93cead6 100644 --- a/src/ScriptCs.Engine.Roslyn/RoslynScriptInMemoryEngine.cs +++ b/src/ScriptCs.Engine.Roslyn/CSharpScriptInMemoryEngine.cs @@ -1,15 +1,18 @@ using System; using System.Reflection; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Engine.Roslyn { - public class RoslynScriptInMemoryEngine : RoslynScriptCompilerEngine + public class CSharpScriptInMemoryEngine : CSharpScriptCompilerEngine { - public RoslynScriptInMemoryEngine(IScriptHostFactory scriptHostFactory, ILog logger) - : base(scriptHostFactory, logger) + private readonly ILog _log; + + public CSharpScriptInMemoryEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider) + : base(scriptHostFactory, logProvider) { + Guard.AgainstNullArgument("logProvider", logProvider); + _log = logProvider.ForCurrentType(); } protected override bool ShouldCompile() @@ -19,12 +22,12 @@ protected override bool ShouldCompile() protected override Assembly LoadAssemblyFromCache() { - throw new NotImplementedException("Reaching this point indicates a bug. The RoslynScriptInMemoryEngine should never load the assembly from the cache."); + throw new NotImplementedException("Reaching this point indicates a bug. The CSharpScriptInMemoryEngine should never load the assembly from the cache."); } protected override Assembly LoadAssembly(byte[] exeBytes, byte[] pdbBytes) { - this.Logger.Debug("Loading assembly from memory."); + _log.Debug("Loading assembly from memory."); // this is required for debugging. otherwise, the .dll is not related to the .pdb // there might be ways of doing this without "loading", haven't found one yet diff --git a/src/ScriptCs.Engine.Roslyn/CommonScriptEngine.cs b/src/ScriptCs.Engine.Roslyn/CommonScriptEngine.cs new file mode 100644 index 00000000..5c482533 --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/CommonScriptEngine.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using ScriptCs.Contracts; + +namespace ScriptCs.Engine.Roslyn +{ + // note this class is a base for future VB engine + public abstract class CommonScriptEngine : IScriptEngine + { + protected ScriptOptions ScriptOptions { get; set; } + + protected ScriptMetadataResolver ScriptMetadataResolver { get; private set; } + + private readonly IScriptHostFactory _scriptHostFactory; + protected ILog Log; + + public const string SessionKey = "Session"; + + protected CommonScriptEngine(IScriptHostFactory scriptHostFactory, ILogProvider logProvider) + { + Guard.AgainstNullArgument("logProvider", logProvider); + ScriptMetadataResolver = ScriptMetadataResolver.Default; + ScriptOptions = ScriptOptions.Default. + WithReferences(typeof(object).Assembly, typeof(TupleElementNamesAttribute).Assembly). // System.ValueTuple + WithMetadataResolver(ScriptMetadataResolver); + _scriptHostFactory = scriptHostFactory; + Log = logProvider.ForCurrentType(); + SetCSharpVersionToLatest(); + } + + public string BaseDirectory + { + get => ScriptMetadataResolver.BaseDirectory; + set => ScriptMetadataResolver = ScriptMetadataResolver.WithBaseDirectory(value); + } + + public string CacheDirectory { get; set; } + + public string FileName { get; set; } + + public string ScriptPath { get; set; } + + public ScriptResult Execute(string code, string[] scriptArgs, AssemblyReferences references, IEnumerable namespaces, ScriptPackSession scriptPackSession) + { + if (scriptPackSession == null) + { + throw new ArgumentNullException(nameof(scriptPackSession)); + } + + if (references == null) + { + throw new ArgumentNullException(nameof(references)); + } + + Log.Debug("Starting to create execution components"); + Log.Debug("Creating script host"); + + var executionReferences = new AssemblyReferences(references.Assemblies, references.Paths); + executionReferences.Union(scriptPackSession.References); + + ScriptResult scriptResult; + SessionState sessionState; + + var isFirstExecution = !scriptPackSession.State.ContainsKey(SessionKey); + + if (isFirstExecution) + { + var host = _scriptHostFactory.CreateScriptHost( + new ScriptPackManager(scriptPackSession.Contexts), scriptArgs); + + ScriptLibraryWrapper.SetHost(host); + Log.Debug("Creating session"); + + var hostType = host.GetType(); + + ScriptOptions = ScriptOptions.AddReferences(hostType.Assembly); + + var allNamespaces = namespaces.Union(scriptPackSession.Namespaces).Distinct(); + + foreach (var reference in executionReferences.Paths) + { + Log.DebugFormat("Adding reference to {0}", reference); + ScriptOptions = ScriptOptions.AddReferences(reference); + } + + foreach (var assembly in executionReferences.Assemblies) + { + Log.DebugFormat("Adding reference to {0}", assembly.FullName); + ScriptOptions = ScriptOptions.AddReferences(assembly); + } + + foreach (var @namespace in allNamespaces) + { + Log.DebugFormat("Importing namespace {0}", @namespace); + ScriptOptions = ScriptOptions.AddImports(@namespace); + } + + sessionState = new SessionState { References = executionReferences, Namespaces = new HashSet(allNamespaces) }; + scriptPackSession.State[SessionKey] = sessionState; + + scriptResult = Execute(code, host, sessionState); + } + else + { + Log.Debug("Reusing existing session"); + sessionState = (SessionState)scriptPackSession.State[SessionKey]; + + if (sessionState.References == null) + { + sessionState.References = new AssemblyReferences(); + } + + if (sessionState.Namespaces == null) + { + sessionState.Namespaces = new HashSet(); + } + + var newReferences = executionReferences.Except(sessionState.References); + + foreach (var reference in newReferences.Paths) + { + Log.DebugFormat("Adding reference to {0}", reference); + ScriptOptions = ScriptOptions.AddReferences(reference); + sessionState.References = sessionState.References.Union(new[] { reference }); + } + + foreach (var assembly in newReferences.Assemblies) + { + Log.DebugFormat("Adding reference to {0}", assembly.FullName); + ScriptOptions = ScriptOptions.AddReferences(assembly); + sessionState.References = sessionState.References.Union(new[] { assembly }); + } + + var newNamespaces = namespaces.Except(sessionState.Namespaces); + + foreach (var @namespace in newNamespaces) + { + Log.DebugFormat("Importing namespace {0}", @namespace); + ScriptOptions = ScriptOptions.AddImports(@namespace); + sessionState.Namespaces.Add(@namespace); + } + + if (string.IsNullOrWhiteSpace(code)) + { + return ScriptResult.Empty; + } + + scriptResult = Execute(code, sessionState.Session, sessionState); + } + + return scriptResult; + + //todo handle namespace failures + //https://github.com/dotnet/roslyn/issues/1012 + } + + protected virtual ScriptResult Execute(string code, object globals, SessionState sessionState) + { + try + { + Log.Debug("Starting execution"); + var result = GetScriptState(code, globals); + Log.Debug("Finished execution"); + sessionState.Session = result; + return new ScriptResult(returnValue: result.ReturnValue); + } + catch (AggregateException ex) + { + return new ScriptResult(executionException: ex.InnerException); + } + catch (CompilationErrorException ex) + { + return new ScriptResult(compilationException: ex); + } + catch (Exception ex) + { + return new ScriptResult(executionException: ex); + } + } + + protected abstract ScriptState GetScriptState(string code, object globals); + + private void SetCSharpVersionToLatest() + { + try + { + // reset default scripting mode to latest language version to enable C# 7.1 features + // this is not needed once https://github.com/dotnet/roslyn/pull/21331 ships + var csharpScriptCompilerType = typeof(CSharpScript).GetTypeInfo().Assembly.GetType("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScriptCompiler"); + var parseOptionsField = csharpScriptCompilerType?.GetField("s_defaultOptions", BindingFlags.Static | BindingFlags.NonPublic); + parseOptionsField?.SetValue(null, new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script)); + } + catch (Exception) + { + Log.Warn("Unable to set C# language version to latest, will use the default version."); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/Properties/AssemblyInfo.cs b/src/ScriptCs.Engine.Roslyn/Properties/AssemblyInfo.cs deleted file mode 100644 index 75a66a88..00000000 --- a/src/ScriptCs.Engine.Roslyn/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("ScriptCs.Engine.Roslyn")] -[assembly: AssemblyDescription("ScriptCs.Engine.Roslyn provides a Roslyn-based script engine for scriptcs.")] diff --git a/src/ScriptCs.Engine.Roslyn/ReplEngineExtensions.cs b/src/ScriptCs.Engine.Roslyn/ReplEngineExtensions.cs new file mode 100644 index 00000000..4b4307c4 --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/ReplEngineExtensions.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis.Scripting; +using ScriptCs.Contracts; + +namespace ScriptCs.Engine.Roslyn +{ + public static class ReplEngineExtensions + { + public static ICollection GetLocalVariables(this IReplEngine replEngine, string sessionKey, + ScriptPackSession scriptPackSession) + { + if (scriptPackSession != null && scriptPackSession.State.ContainsKey(sessionKey)) + { + var sessionState = (SessionState)scriptPackSession.State[sessionKey]; + return sessionState.Session.Variables.Select(x => $"{x.Type} {x.Name}").Distinct().ToArray(); + } + + return new string[0]; + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/RoslynModule.cs b/src/ScriptCs.Engine.Roslyn/RoslynModule.cs index 3d4da70f..4fdb0196 100644 --- a/src/ScriptCs.Engine.Roslyn/RoslynModule.cs +++ b/src/ScriptCs.Engine.Roslyn/RoslynModule.cs @@ -11,9 +11,9 @@ public void Initialize(IModuleConfiguration config) if (!config.Overrides.ContainsKey(typeof(IScriptEngine))) { - var engineType = config.Cache ? typeof(RoslynScriptPersistentEngine) : typeof(RoslynScriptEngine); - engineType = config.Debug ? typeof(RoslynScriptInMemoryEngine) : engineType; - engineType = config.IsRepl ? typeof(RoslynReplEngine) : engineType; + var engineType = config.Cache ? typeof(CSharpPersistentEngine) : typeof(CSharpScriptEngine); + engineType = config.Debug ? typeof(CSharpScriptInMemoryEngine) : engineType; + engineType = config.IsRepl ? typeof(CSharpReplEngine) : engineType; config.Overrides[typeof(IScriptEngine)] = engineType; } } diff --git a/src/ScriptCs.Engine.Roslyn/RoslynReplEngine.cs b/src/ScriptCs.Engine.Roslyn/RoslynReplEngine.cs deleted file mode 100644 index 031583df..00000000 --- a/src/ScriptCs.Engine.Roslyn/RoslynReplEngine.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using Roslyn.Scripting; -using ScriptCs.Contracts; -using ScriptCs.Logging; - -namespace ScriptCs.Engine.Roslyn -{ - using System.Globalization; - - public class RoslynReplEngine : RoslynScriptEngine, IReplEngine - { - public RoslynReplEngine(IScriptHostFactory scriptHostFactory, ILog logger) - : base(scriptHostFactory, logger) - { - } - - public ICollection GetLocalVariables(ScriptPackSession scriptPackSession) - { - var variables = new Collection(); - if (scriptPackSession != null && scriptPackSession.State.ContainsKey(SessionKey)) - { - var sessionState = (SessionState)scriptPackSession.State[SessionKey]; - var submissionObjectField = sessionState.Session.GetType() - .GetField("submissions", BindingFlags.Instance | BindingFlags.NonPublic); - - if (submissionObjectField != null) - { - var submissionObjectFieldValue = submissionObjectField.GetValue(sessionState.Session); - if (submissionObjectFieldValue != null) - { - var submissionObjects = submissionObjectFieldValue as object[]; - - if (submissionObjects != null && submissionObjects.Any(x => x != null)) - { - var processedFields = new Collection(); - - // reversing to get the latest submission first - foreach (var submissionObject in submissionObjects.Where(x => x != null).Reverse()) - { - foreach (var field in submissionObject.GetType().GetFields() - .Where(x => x.Name.ToLowerInvariant() != "") - .Where(field => !processedFields.Contains(field.Name))) - { - var variable = string.Format( - CultureInfo.InvariantCulture, - "{0} {1} = {2}", - field.FieldType, - field.Name, - field.GetValue(submissionObject)); - - variables.Add(variable); - processedFields.Add(field.Name); - } - } - } - } - - } - } - - return variables; - } - - protected override ScriptResult Execute(string code, Session session) - { - Guard.AgainstNullArgument("session", session); - - return string.IsNullOrWhiteSpace(FileName) && !IsCompleteSubmission(code) - ? ScriptResult.Incomplete - : base.Execute(code, session); - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/RoslynScriptCompilerEngine.cs b/src/ScriptCs.Engine.Roslyn/RoslynScriptCompilerEngine.cs deleted file mode 100644 index b06a09f9..00000000 --- a/src/ScriptCs.Engine.Roslyn/RoslynScriptCompilerEngine.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using Roslyn.Scripting; -using ScriptCs.Contracts; -using ScriptCs.Exceptions; -using ScriptCs.Logging; - -namespace ScriptCs.Engine.Roslyn -{ - public abstract class RoslynScriptCompilerEngine : RoslynScriptEngine - { - private const string CompiledScriptClass = "Submission#0"; - - private const string CompiledScriptMethod = ""; - - protected RoslynScriptCompilerEngine(IScriptHostFactory scriptHostFactory, ILog logger) - : base(scriptHostFactory, logger) - { - } - - protected abstract bool ShouldCompile(); - - protected abstract Assembly LoadAssembly(byte[] exeBytes, byte[] pdbBytes); - - protected abstract Assembly LoadAssemblyFromCache(); - - protected override ScriptResult Execute(string code, Session session) - { - Guard.AgainstNullArgument("session", session); - - return ShouldCompile() - ? CompileAndExecute(code, session) - : InvokeEntryPointMethod(session, LoadAssemblyFromCache()); - } - - private ScriptResult CompileAndExecute(string code, Session session) - { - Logger.Debug("Compiling submission"); - try - { - var submission = session.CompileSubmission(code); - - using (var exeStream = new MemoryStream()) - using (var pdbStream = new MemoryStream()) - { - var result = submission.Compilation.Emit(exeStream, pdbStream: pdbStream); - - if (result.Success) - { - Logger.Debug("Compilation was successful."); - - var assembly = LoadAssembly(exeStream.ToArray(), pdbStream.ToArray()); - - return InvokeEntryPointMethod(session, assembly); - } - - var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x.ToString())); - - Logger.ErrorFormat("Error occurred when compiling: {0})", errors); - - return new ScriptResult(compilationException: new ScriptCompilationException(errors)); - } - } - catch (Exception compileException) - { - //we catch Exception rather than CompilationErrorException because there might be issues with assembly loading too - return new ScriptResult(compilationException: new ScriptCompilationException(compileException.Message, compileException)); - } - } - - private ScriptResult InvokeEntryPointMethod(Session session, Assembly assembly) - { - Logger.Debug("Retrieving compiled script class (reflection)."); - - // the following line can throw NullReferenceException, if that happens it's useful to notify that an error ocurred - var type = assembly.GetType(CompiledScriptClass); - Logger.Debug("Retrieving compiled script method (reflection)."); - var method = type.GetMethod(CompiledScriptMethod, BindingFlags.Static | BindingFlags.Public); - - try - { - Logger.Debug("Invoking method."); - - return new ScriptResult(returnValue: method.Invoke(null, new object[] { session })); - } - catch (Exception executeException) - { - Logger.Error("An error occurred when executing the scripts."); - - var ex = executeException.InnerException ?? executeException; - - return new ScriptResult(executionException: ex); - } - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/RoslynScriptEngine.cs b/src/ScriptCs.Engine.Roslyn/RoslynScriptEngine.cs deleted file mode 100644 index edfc2284..00000000 --- a/src/ScriptCs.Engine.Roslyn/RoslynScriptEngine.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Roslyn.Compilers; -using Roslyn.Compilers.CSharp; -using Roslyn.Scripting; -using Roslyn.Scripting.CSharp; -using ScriptCs.Contracts; -using ScriptCs.Logging; - -namespace ScriptCs.Engine.Roslyn -{ - public class RoslynScriptEngine : IScriptEngine - { - protected readonly ScriptEngine ScriptEngine; - private readonly IScriptHostFactory _scriptHostFactory; - - public const string SessionKey = "Session"; - private const string InvalidNamespaceError = "error CS0246"; - - public RoslynScriptEngine(IScriptHostFactory scriptHostFactory, ILog logger) - { - ScriptEngine = new ScriptEngine(); - _scriptHostFactory = scriptHostFactory; - Logger = logger; - } - - protected ILog Logger { get; private set; } - - public string BaseDirectory - { - get { return ScriptEngine.BaseDirectory; } - set { ScriptEngine.BaseDirectory = value; } - } - - public string CacheDirectory { get; set; } - - public string FileName { get; set; } - - public ScriptResult Execute( - string code, - string[] scriptArgs, - AssemblyReferences references, - IEnumerable namespaces, - ScriptPackSession scriptPackSession) - { - Guard.AgainstNullArgument("scriptPackSession", scriptPackSession); - Guard.AgainstNullArgument("references", references); - - Logger.Debug("Starting to create execution components"); - Logger.Debug("Creating script host"); - - var executionReferences = references.Union(scriptPackSession.References); - - SessionState sessionState; - - var isFirstExecution = !scriptPackSession.State.ContainsKey(SessionKey); - - if (isFirstExecution) - { - code = code.DefineTrace(); - var host = _scriptHostFactory.CreateScriptHost( - new ScriptPackManager(scriptPackSession.Contexts), scriptArgs); - - ScriptLibraryWrapper.SetHost(host); - Logger.Debug("Creating session"); - - var hostType = host.GetType(); - ScriptEngine.AddReference(hostType.Assembly); - var session = ScriptEngine.CreateSession(host, hostType); - var allNamespaces = namespaces.Union(scriptPackSession.Namespaces).Distinct(); - - foreach (var reference in executionReferences.Paths) - { - Logger.DebugFormat("Adding reference to {0}", reference); - session.AddReference(reference); - } - - foreach (var assembly in executionReferences.Assemblies) - { - Logger.DebugFormat("Adding reference to {0}", assembly.FullName); - session.AddReference(assembly); - } - - foreach (var @namespace in allNamespaces) - { - Logger.DebugFormat("Importing namespace {0}", @namespace); - session.ImportNamespace(@namespace); - } - - sessionState = new SessionState - { - References = executionReferences, - Session = session, - Namespaces = new HashSet(allNamespaces) - }; - - scriptPackSession.State[SessionKey] = sessionState; - } - else - { - Logger.Debug("Reusing existing session"); - sessionState = (SessionState)scriptPackSession.State[SessionKey]; - - if (sessionState.References == null) - { - sessionState.References = new AssemblyReferences(); - } - - if (sessionState.Namespaces == null) - { - sessionState.Namespaces = new HashSet(); - } - - var newReferences = executionReferences.Except(sessionState.References); - - foreach (var reference in newReferences.Paths) - { - Logger.DebugFormat("Adding reference to {0}", reference); - sessionState.Session.AddReference(reference); - sessionState.References = sessionState.References.Union(new[] { reference }); - } - - foreach (var assembly in newReferences.Assemblies) - { - Logger.DebugFormat("Adding reference to {0}", assembly.FullName); - sessionState.Session.AddReference(assembly); - sessionState.References = sessionState.References.Union(new[] { assembly }); - } - - var newNamespaces = namespaces.Except(sessionState.Namespaces); - - foreach (var @namespace in newNamespaces) - { - Logger.DebugFormat("Importing namespace {0}", @namespace); - sessionState.Session.ImportNamespace(@namespace); - sessionState.Namespaces.Add(@namespace); - } - } - - Logger.Debug("Starting execution"); - - var result = Execute(code, sessionState.Session); - - if (result.InvalidNamespaces.Any()) - { - var pendingNamespacesField = sessionState.Session.GetType().GetField( - "pendingNamespaces", - System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); - - if (pendingNamespacesField != null) - { - var pendingNamespacesValue = (ReadOnlyArray)pendingNamespacesField - .GetValue(sessionState.Session); - - //no need to check this for null as ReadOnlyArray is a value type - if (pendingNamespacesValue.Any()) - { - var fixedNamespaces = pendingNamespacesValue.ToList(); - - foreach (var @namespace in result.InvalidNamespaces) - { - sessionState.Namespaces.Remove(@namespace); - fixedNamespaces.Remove(@namespace); - } - pendingNamespacesField.SetValue( - sessionState.Session, ReadOnlyArray.CreateFrom(fixedNamespaces)); - } - } - } - - Logger.Debug("Finished execution"); - return result; - } - - protected virtual ScriptResult Execute(string code, Session session) - { - Guard.AgainstNullArgument("session", session); - - try - { - var submission = session.CompileSubmission(code); - - try - { - return new ScriptResult(submission.Execute()); - } - catch (AggregateException ex) - { - return new ScriptResult(executionException: ex.InnerException); - } - catch (Exception ex) - { - return new ScriptResult(executionException: ex); - } - } - catch (Exception ex) - { - if (ex.Message.StartsWith(InvalidNamespaceError)) - { - var offendingNamespace = Regex.Match(ex.Message, @"\'([^']*)\'").Groups[1].Value; - return new ScriptResult( - compilationException: ex, invalidNamespaces: new string[1] { offendingNamespace }); - } - - return new ScriptResult(compilationException: ex); - } - } - - protected static bool IsCompleteSubmission(string code) - { - var options = new ParseOptions( - CompatibilityMode.None, - LanguageVersion.CSharp4, - true, - SourceCodeKind.Interactive); - - var syntaxTree = SyntaxTree.ParseText(code, options: options); - - return Syntax.IsCompleteSubmission(syntaxTree); - } - } -} diff --git a/src/ScriptCs.Engine.Roslyn/ScriptCs.Engine.Roslyn.csproj b/src/ScriptCs.Engine.Roslyn/ScriptCs.Engine.Roslyn.csproj index bc128bf9..e88e280a 100644 --- a/src/ScriptCs.Engine.Roslyn/ScriptCs.Engine.Roslyn.csproj +++ b/src/ScriptCs.Engine.Roslyn/ScriptCs.Engine.Roslyn.csproj @@ -1,93 +1,25 @@ - - - + - Debug - AnyCPU - {E79EC231-E27D-4057-91C9-2D001A3A8C3B} - Library - ScriptCs.Engine.Roslyn - ScriptCs.Engine.Roslyn - Properties - 512 - v4.5 - - ..\..\ + 1.0.0 + netstandard2.0 + ScriptCs.Engine.Roslyn + Glenn Block, Filip Wojcieszyn, Justin Rusbatch + https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md + http://scriptcs.net + http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 + ScriptCs.Engine.Roslyn + ScriptCs.Engine.Roslyn provides a Roslyn-based script engine for scriptcs. + roslyn csx script scriptcs - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - - ..\..\packages\Roslyn.Compilers.Common.1.2.20906.2\lib\net45\Roslyn.Compilers.dll - - - ..\..\packages\Roslyn.Compilers.CSharp.1.2.20906.2\lib\net45\Roslyn.Compilers.CSharp.dll - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - Guard.cs - - - - - - - - + - - + + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - + + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/ScriptCs.Engine.Roslyn/app.config b/src/ScriptCs.Engine.Roslyn/app.config new file mode 100644 index 00000000..d97e7d25 --- /dev/null +++ b/src/ScriptCs.Engine.Roslyn/app.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/ScriptCs.Engine.Roslyn/packages.config b/src/ScriptCs.Engine.Roslyn/packages.config deleted file mode 100644 index cfd383e1..00000000 --- a/src/ScriptCs.Engine.Roslyn/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ColoredConsoleLogProvider.cs b/src/ScriptCs.Hosting/ColoredConsoleLogProvider.cs new file mode 100644 index 00000000..94dd7d23 --- /dev/null +++ b/src/ScriptCs.Hosting/ColoredConsoleLogProvider.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using ScriptCs.Contracts; + +namespace ScriptCs.Hosting +{ + public class ColoredConsoleLogProvider : ILogProvider + { + private static readonly Disposable disposable = new Disposable(); + private static readonly Dictionary colors = + new Dictionary + { + { LogLevel.Fatal, ConsoleColor.Red }, + { LogLevel.Error, ConsoleColor.Red }, + { LogLevel.Warn, ConsoleColor.DarkYellow }, + { LogLevel.Info, ConsoleColor.Gray }, + { LogLevel.Debug, ConsoleColor.DarkGray }, + { LogLevel.Trace, ConsoleColor.DarkMagenta }, + }; + + private readonly LogLevel _logLevel; + private readonly IConsole _console; + + public ColoredConsoleLogProvider(LogLevel logLevel, IConsole console) + { + Guard.AgainstNullArgument("console", console); + + _logLevel = logLevel; + _console = console; + } + + public Logger GetLogger(string name) + { + return (logLevel, messageFunc, exception, formatParameters) => + Log(name, logLevel, messageFunc, exception, formatParameters); + } + + public IDisposable OpenNestedContext(string message) => disposable; + + public IDisposable OpenMappedContext(string key, string value) => disposable; + + public bool Log( + string name, LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + Guard.AgainstNullArgument("formatParameters", formatParameters); + + var isEnabled = IsEnabled(logLevel); + if (!isEnabled || messageFunc == null) + { + return isEnabled; + } + + var prefix = logLevel == LogLevel.Info + ? null + : string.Concat(logLevel.ToString().ToUpperInvariant(), ": "); + + if (_logLevel == LogLevel.Debug || _logLevel == LogLevel.Trace) + { + prefix = string.Concat(prefix, "[", name, "] "); + } + + var message = string.Format(CultureInfo.InvariantCulture, messageFunc(), formatParameters); + + var suffix = string.Empty; + if (exception != null) + { + if (_logLevel == LogLevel.Debug || _logLevel == LogLevel.Trace) + { + var exceptions = new List(); + while (exception != null) + { + var exceptionString = string.Format( + CultureInfo.InvariantCulture, + "[{0}] {1}{2}{3}", + exception.GetType().FullName, + exception.Message, + Environment.NewLine, + exception.StackTrace); + + exceptions.Add(exceptionString); + exception = exception.InnerException; + } + + var divider = string.Format( + CultureInfo.InvariantCulture, "{0}{1}{0}", Environment.NewLine, "=== INNER EXCEPTION ==="); + + suffix = " " + string.Join(divider, exceptions); + } + else + { + suffix = string.Format( + CultureInfo.InvariantCulture, " [{0}] {1}", exception.GetType().Name, exception.Message); + } + } + + ConsoleColor color; + if (!colors.TryGetValue(logLevel, out color)) + { + color = ConsoleColor.White; + } + + var originalColor = _console.ForegroundColor; + _console.ForegroundColor = color; + try + { + _console.WriteLine(string.Concat(prefix, message, suffix)); + } + finally + { + _console.ForegroundColor = originalColor; + } + + return true; + } + + private bool IsEnabled(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Fatal: + return true; + case LogLevel.Error: + return + _logLevel == LogLevel.Error || + _logLevel == LogLevel.Warn || + _logLevel == LogLevel.Info || + _logLevel == LogLevel.Debug || + _logLevel == LogLevel.Trace; + case LogLevel.Warn: + return + _logLevel == LogLevel.Warn || + _logLevel == LogLevel.Info || + _logLevel == LogLevel.Debug || + _logLevel == LogLevel.Trace; + case LogLevel.Info: + return + _logLevel == LogLevel.Info || + _logLevel == LogLevel.Debug || + _logLevel == LogLevel.Trace; + case LogLevel.Debug: + return + _logLevel == LogLevel.Debug || + _logLevel == LogLevel.Trace; + case LogLevel.Trace: + return + _logLevel == LogLevel.Trace; + } + + return true; + } + + private sealed class Disposable : IDisposable + { + public void Dispose() + { + } + } + } +} diff --git a/src/ScriptCs.Hosting/DirectoryInfo.cs b/src/ScriptCs.Hosting/DirectoryInfo.cs new file mode 100644 index 00000000..dff1ed75 --- /dev/null +++ b/src/ScriptCs.Hosting/DirectoryInfo.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptCs.Hosting +{ + public class DirectoryInfo + { + public DirectoryInfo() + { + Guid = Guid.NewGuid(); + Files = new List(); + Directories = new Dictionary(); + } + + public Guid Guid { get; private set; } + public string Name { get; set; } + public string Path { get; set; } + public string FullPath { get; set; } + public List Files { get; private set; } + public Dictionary Directories { get; private set; } + } +} diff --git a/src/ScriptCs.Hosting/FileConsole.cs b/src/ScriptCs.Hosting/FileConsole.cs index dbac0953..984b51ed 100644 --- a/src/ScriptCs.Hosting/FileConsole.cs +++ b/src/ScriptCs.Hosting/FileConsole.cs @@ -35,9 +35,9 @@ public void WriteLine(string value) this.AppendLine(value); } - public string ReadLine() + public string ReadLine(string prompt) { - var line = _innerConsole.ReadLine(); + var line = _innerConsole.ReadLine(""); this.AppendLine(line); return line; } @@ -59,10 +59,12 @@ public void ResetColor() public ConsoleColor ForegroundColor { - get { return _innerConsole.ForegroundColor; } - set { _innerConsole.ForegroundColor = value; } + get => _innerConsole.ForegroundColor; + set => _innerConsole.ForegroundColor = value; } + public int Width => int.MaxValue; + private void Append(string text) { using (var writer = new StreamWriter(_path, true)) diff --git a/src/ScriptCs.Hosting/IInitializationServices.cs b/src/ScriptCs.Hosting/IInitializationServices.cs index fd1684f5..66ae6e88 100644 --- a/src/ScriptCs.Hosting/IInitializationServices.cs +++ b/src/ScriptCs.Hosting/IInitializationServices.cs @@ -1,5 +1,5 @@ -using ScriptCs.Contracts; -using ScriptCs.Logging; +using System; +using ScriptCs.Contracts; namespace ScriptCs.Hosting { @@ -17,7 +17,7 @@ public interface IInitializationServices IPackageInstaller GetPackageInstaller(); - ILog Logger { get; } + ILogProvider LogProvider { get; } IAppDomainAssemblyResolver GetAppDomainAssemblyResolver(); diff --git a/src/ScriptCs.Hosting/IVisualStudioSolution.cs b/src/ScriptCs.Hosting/IVisualStudioSolution.cs new file mode 100644 index 00000000..91eb6111 --- /dev/null +++ b/src/ScriptCs.Hosting/IVisualStudioSolution.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace ScriptCs.Contracts +{ + public interface IVisualStudioSolution + { + void AddHeader(); + void AddScriptcsProject(string scriptcsPath, string workingPath, string args, bool attach, Guid projectGuid); + void AddProject(string path, string name, Guid guid, string[] files); + void AddGlobalHeader(Guid projectGuid); + void AddGlobalNestedProjects(IList nestedItems); + void AddGlobal(Guid projectGuid, IList nestedItems); + } +} \ No newline at end of file diff --git a/src/ScriptCs.Hosting/IVisualStudioSolutionWriter.cs b/src/ScriptCs.Hosting/IVisualStudioSolutionWriter.cs new file mode 100644 index 00000000..c893902e --- /dev/null +++ b/src/ScriptCs.Hosting/IVisualStudioSolutionWriter.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using ScriptCs.Contracts; + +namespace ScriptCs.Contracts +{ + public interface IVisualStudioSolutionWriter + { + string WriteSolution(IFileSystem fs, string script, IVisualStudioSolution solution, IList nestedItems = null); + } +} \ No newline at end of file diff --git a/src/ScriptCs.Hosting/InitializationServices.cs b/src/ScriptCs.Hosting/InitializationServices.cs index 743ee9d3..8e0fa92c 100644 --- a/src/ScriptCs.Hosting/InitializationServices.cs +++ b/src/ScriptCs.Hosting/InitializationServices.cs @@ -3,22 +3,27 @@ using Autofac; using ScriptCs.Contracts; using ScriptCs.Hosting.Package; -using ScriptCs.Logging; namespace ScriptCs.Hosting { public class InitializationServices : ScriptServicesRegistration, IInitializationServices { - public InitializationServices(ILog logger, IDictionary overrides = null) - : base(logger, overrides) + private readonly ILog _log; + + public InitializationServices(ILogProvider logProvider, IDictionary overrides = null) + : base(logProvider, overrides) { + Guard.AgainstNullArgument("logProvider", logProvider); + + _log = logProvider.ForCurrentType(); + } protected override IContainer CreateContainer() { var builder = new ContainerBuilder(); - this.Logger.Debug("Registering initialization services"); - builder.RegisterInstance(this.Logger); + _log.Debug("Registering initialization services"); + builder.RegisterInstance(this.LogProvider); builder.RegisterType().As(); RegisterLineProcessors(builder); @@ -106,7 +111,7 @@ private T GetService(ref T service ) { if (Equals(service,null)) { - this.Logger.Debug(string.Format("Resolving {0}", typeof(T).Name)); + _log.Debug(string.Format("Resolving {0}", typeof(T).Name)); service = Container.Resolve(); } diff --git a/src/ScriptCs.Hosting/LineEditor.cs b/src/ScriptCs.Hosting/LineEditor.cs new file mode 100644 index 00000000..7c2ae132 --- /dev/null +++ b/src/ScriptCs.Hosting/LineEditor.cs @@ -0,0 +1,1173 @@ +// +// getline.cs: A command line editor +// +// Authors: +// Miguel de Icaza (miguel@novell.com) +// +// Copyright 2008 Novell, Inc. +// +// Dual-licensed under the terms of the MIT X11 license or the +// Apache License 2.0 +// +// USE -define:DEMO to build this as a standalone file and test it +// +// TODO: +// Enter an error (a = 1); Notice how the prompt is in the wrong line +// This is caused by Stderr not being tracked by System.Console. +// Completion support +// Why is Thread.Interrupt not working? Currently I resort to Abort which is too much. +// +// Limitations in System.Console: +// Console needs SIGWINCH support of some sort +// Console needs a way of updating its position after things have been written +// behind its back (P/Invoke puts for example). +// System.Console needs to get the DELETE character, and report accordingly. +// + +using System; +using System.Text; +using System.IO; +using System.Threading; +using System.Reflection; + +namespace Mono.Terminal +{ + public class LineEditor + { + public class Completion + { + public string[] Result; + public string Prefix; + + public Completion(string prefix, string[] result) + { + Prefix = prefix; + Result = result; + } + } + + public delegate Completion AutoCompleteHandler(string text, int pos); + + //static StreamWriter log; + + // The text being edited. + StringBuilder text; + + // The text as it is rendered (replaces (char)1 with ^A on display for example). + StringBuilder rendered_text; + + // The prompt specified, and the prompt shown to the user. + string prompt; + string shown_prompt; + + // The current cursor position, indexes into "text", for an index + // into rendered_text, use TextToRenderPos + int cursor; + + // The row where we started displaying data. + int home_row; + + // The maximum length that has been displayed on the screen + int max_rendered; + + // If we are done editing, this breaks the interactive loop + bool done = false; + + // The thread where the Editing started taking place + Thread edit_thread; + + // Our object that tracks history + History history; + + // The contents of the kill buffer (cut/paste in Emacs parlance) + string kill_buffer = ""; + + // The string being searched for + string search; + string last_search; + + // whether we are searching (-1= reverse; 0 = no; 1 = forward) + int searching; + + // The position where we found the match. + int match_at; + + // Used to implement the Kill semantics (multiple Alt-Ds accumulate) + KeyHandler last_handler; + + delegate void KeyHandler(); + + struct Handler + { + public ConsoleKeyInfo CKI; + public KeyHandler KeyHandler; + + public Handler(ConsoleKey key, KeyHandler h) + { + CKI = new ConsoleKeyInfo((char)0, key, false, false, false); + KeyHandler = h; + } + + public Handler(char c, KeyHandler h) + { + KeyHandler = h; + // Use the "Zoom" as a flag that we only have a character. + CKI = new ConsoleKeyInfo(c, ConsoleKey.Zoom, false, false, false); + } + + public Handler(ConsoleKeyInfo cki, KeyHandler h) + { + CKI = cki; + KeyHandler = h; + } + + public static Handler Control(char c, KeyHandler h) + { + return new Handler((char)(c - 'A' + 1), h); + } + + public static Handler Alt(char c, ConsoleKey k, KeyHandler h) + { + ConsoleKeyInfo cki = new ConsoleKeyInfo((char)c, k, false, true, false); + return new Handler(cki, h); + } + } + + /// + /// Invoked when the user requests auto-completion using the tab character + /// + /// + /// The result is null for no values found, an array with a single + /// string, in that case the string should be the text to be inserted + /// for example if the word at pos is "T", the result for a completion + /// of "ToString" should be "oString", not "ToString". + /// + /// When there are multiple results, the result should be the full + /// text + /// + public AutoCompleteHandler AutoCompleteEvent; + + static Handler[] handlers; + + public LineEditor(string name) : this(name, 10) { } + + public LineEditor(string name, int histsize) + { + handlers = new Handler[] { + new Handler (ConsoleKey.Home, CmdHome), + new Handler (ConsoleKey.End, CmdEnd), + new Handler (ConsoleKey.LeftArrow, CmdLeft), + new Handler (ConsoleKey.RightArrow, CmdRight), + new Handler (ConsoleKey.UpArrow, CmdHistoryPrev), + new Handler (ConsoleKey.DownArrow, CmdHistoryNext), + new Handler (ConsoleKey.Enter, CmdDone), + new Handler (ConsoleKey.Backspace, CmdBackspace), + new Handler (ConsoleKey.Delete, CmdDeleteChar), + new Handler (ConsoleKey.Tab, CmdTabOrComplete), + + // Emacs keys + Handler.Control ('A', CmdHome), + Handler.Control ('E', CmdEnd), + Handler.Control ('B', CmdLeft), + Handler.Control ('F', CmdRight), + Handler.Control ('P', CmdHistoryPrev), + Handler.Control ('N', CmdHistoryNext), + Handler.Control ('K', CmdKillToEOF), + Handler.Control ('Y', CmdYank), + Handler.Control ('D', CmdDeleteChar), + Handler.Control ('L', CmdRefresh), + Handler.Control ('R', CmdReverseSearch), + Handler.Control ('G', delegate {} ), + Handler.Alt ('B', ConsoleKey.B, CmdBackwardWord), + Handler.Alt ('F', ConsoleKey.F, CmdForwardWord), + + Handler.Alt ('D', ConsoleKey.D, CmdDeleteWord), + Handler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword), + + // DEBUG + //Handler.Control ('T', CmdDebug), + + // quote + Handler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); }) + }; + + rendered_text = new StringBuilder(); + text = new StringBuilder(); + + history = new History(name, histsize); + + //if (File.Exists ("log"))File.Delete ("log"); + //log = File.CreateText ("log"); + } + + void CmdDebug() + { + history.Dump(); + Console.WriteLine(); + Render(); + } + + void Render() + { + Console.Write(shown_prompt); + Console.Write(rendered_text); + + int max = System.Math.Max(rendered_text.Length + shown_prompt.Length, max_rendered); + + for (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++) + Console.Write(' '); + max_rendered = shown_prompt.Length + rendered_text.Length; + + // Write one more to ensure that we always wrap around properly if we are at the + // end of a line. + Console.Write(' '); + + UpdateHomeRow(max); + } + + void UpdateHomeRow(int screenpos) + { + int lines = 1 + (screenpos / Console.WindowWidth); + + home_row = Console.CursorTop - (lines - 1); + if (home_row < 0) + home_row = 0; + } + + + void RenderFrom(int pos) + { + int rpos = TextToRenderPos(pos); + int i; + + for (i = rpos; i < rendered_text.Length; i++) + Console.Write(rendered_text[i]); + + if ((shown_prompt.Length + rendered_text.Length) > max_rendered) + max_rendered = shown_prompt.Length + rendered_text.Length; + else + { + int max_extra = max_rendered - shown_prompt.Length; + for (; i < max_extra; i++) + Console.Write(' '); + } + } + + void ComputeRendered() + { + rendered_text.Length = 0; + + for (int i = 0; i < text.Length; i++) + { + int c = (int)text[i]; + if (c < 26) + { + if (c == '\t') + rendered_text.Append(" "); + else + { + rendered_text.Append('^'); + rendered_text.Append((char)(c + (int)'A' - 1)); + } + } + else + rendered_text.Append((char)c); + } + } + + int TextToRenderPos(int pos) + { + int p = 0; + + for (int i = 0; i < pos; i++) + { + int c; + + c = (int)text[i]; + + if (c < 26) + { + if (c == 9) + p += 4; + else + p += 2; + } + else + p++; + } + + return p; + } + + int TextToScreenPos(int pos) + { + return shown_prompt.Length + TextToRenderPos(pos); + } + + string Prompt + { + get => prompt; + set => prompt = value; + } + + int LineCount => (shown_prompt.Length + rendered_text.Length) / Console.WindowWidth; + + void ForceCursor(int newpos) + { + cursor = newpos; + + int actual_pos = shown_prompt.Length + TextToRenderPos(cursor); + int row = home_row + (actual_pos / Console.WindowWidth); + int col = actual_pos % Console.WindowWidth; + + if (row >= Console.BufferHeight) + row = Console.BufferHeight - 1; + Console.SetCursorPosition(col, row); + + //log.WriteLine ("Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor); + //log.Flush (); + } + + void UpdateCursor(int newpos) + { + if (cursor == newpos) + return; + + ForceCursor(newpos); + } + + void InsertChar(char c) + { + int prev_lines = LineCount; + text = text.Insert(cursor, c); + ComputeRendered(); + if (prev_lines != LineCount) + { + + Console.SetCursorPosition(0, home_row); + Render(); + ForceCursor(++cursor); + } + else + { + RenderFrom(cursor); + ForceCursor(++cursor); + UpdateHomeRow(TextToScreenPos(cursor)); + } + } + + // + // Commands + // + void CmdDone() + { + done = true; + } + + void CmdTabOrComplete() + { + bool complete = false; + + if (AutoCompleteEvent != null) + { + if (TabAtStartCompletes) + complete = true; + else + { + for (int i = 0; i < cursor; i++) + { + if (!Char.IsWhiteSpace(text[i])) + { + complete = true; + break; + } + } + } + + if (complete) + { + Completion completion = AutoCompleteEvent(text.ToString(), cursor); + string[] completions = completion.Result; + if (completions == null) + return; + + int ncompletions = completions.Length; + if (ncompletions == 0) + return; + + if (completions.Length == 1) + { + InsertTextAtCursor(completions[0]); + } + else + { + int last = -1; + + for (int p = 0; p < completions[0].Length; p++) + { + char c = completions[0][p]; + + + for (int i = 1; i < ncompletions; i++) + { + if (completions[i].Length < p) + goto mismatch; + + if (completions[i][p] != c) + { + goto mismatch; + } + } + last = p; + } + mismatch: + if (last != -1) + { + InsertTextAtCursor(completions[0].Substring(0, last + 1)); + } + Console.WriteLine(); + foreach (string s in completions) + { + Console.Write(completion.Prefix); + Console.Write(s); + Console.Write(' '); + } + Console.WriteLine(); + Render(); + ForceCursor(cursor); + } + } + else + HandleChar('\t'); + } + else + HandleChar('t'); + } + + void CmdHome() + { + UpdateCursor(0); + } + + void CmdEnd() + { + UpdateCursor(text.Length); + } + + void CmdLeft() + { + if (cursor == 0) + return; + + UpdateCursor(cursor - 1); + } + + void CmdBackwardWord() + { + int p = WordBackward(cursor); + if (p == -1) + return; + UpdateCursor(p); + } + + void CmdForwardWord() + { + int p = WordForward(cursor); + if (p == -1) + return; + UpdateCursor(p); + } + + void CmdRight() + { + if (cursor == text.Length) + return; + + UpdateCursor(cursor + 1); + } + + void RenderAfter(int p) + { + ForceCursor(p); + RenderFrom(p); + ForceCursor(cursor); + } + + void CmdBackspace() + { + if (cursor == 0) + return; + + text.Remove(--cursor, 1); + ComputeRendered(); + RenderAfter(cursor); + } + + void CmdDeleteChar() + { + // If there is no input, this behaves like EOF + if (text.Length == 0) + { + done = true; + text = null; + Console.WriteLine(); + return; + } + + if (cursor == text.Length) + return; + text.Remove(cursor, 1); + ComputeRendered(); + RenderAfter(cursor); + } + + int WordForward(int p) + { + if (p >= text.Length) + return -1; + + int i = p; + if (Char.IsPunctuation(text[p]) || Char.IsSymbol(text[p]) || Char.IsWhiteSpace(text[p])) + { + for (; i < text.Length; i++) + { + if (Char.IsLetterOrDigit(text[i])) + break; + } + for (; i < text.Length; i++) + { + if (!Char.IsLetterOrDigit(text[i])) + break; + } + } + else + { + for (; i < text.Length; i++) + { + if (!Char.IsLetterOrDigit(text[i])) + break; + } + } + if (i != p) + return i; + return -1; + } + + int WordBackward(int p) + { + if (p == 0) + return -1; + + int i = p - 1; + if (i == 0) + return 0; + + if (Char.IsPunctuation(text[i]) || Char.IsSymbol(text[i]) || Char.IsWhiteSpace(text[i])) + { + for (; i >= 0; i--) + { + if (Char.IsLetterOrDigit(text[i])) + break; + } + for (; i >= 0; i--) + { + if (!Char.IsLetterOrDigit(text[i])) + break; + } + } + else + { + for (; i >= 0; i--) + { + if (!Char.IsLetterOrDigit(text[i])) + break; + } + } + i++; + + if (i != p) + return i; + + return -1; + } + + void CmdDeleteWord() + { + int pos = WordForward(cursor); + + if (pos == -1) + return; + + string k = text.ToString(cursor, pos - cursor); + + if (last_handler == CmdDeleteWord) + kill_buffer = kill_buffer + k; + else + kill_buffer = k; + + text.Remove(cursor, pos - cursor); + ComputeRendered(); + RenderAfter(cursor); + } + + void CmdDeleteBackword() + { + int pos = WordBackward(cursor); + if (pos == -1) + return; + + string k = text.ToString(pos, cursor - pos); + + if (last_handler == CmdDeleteBackword) + kill_buffer = k + kill_buffer; + else + kill_buffer = k; + + text.Remove(pos, cursor - pos); + ComputeRendered(); + RenderAfter(pos); + } + + // + // Adds the current line to the history if needed + // + void HistoryUpdateLine() + { + history.Update(text.ToString()); + } + + void CmdHistoryPrev() + { + if (!history.PreviousAvailable()) + return; + + HistoryUpdateLine(); + + SetText(history.Previous()); + } + + void CmdHistoryNext() + { + if (!history.NextAvailable()) + return; + + history.Update(text.ToString()); + SetText(history.Next()); + + } + + void CmdKillToEOF() + { + kill_buffer = text.ToString(cursor, text.Length - cursor); + text.Length = cursor; + ComputeRendered(); + RenderAfter(cursor); + } + + void CmdYank() + { + InsertTextAtCursor(kill_buffer); + } + + void InsertTextAtCursor(string str) + { + int prev_lines = LineCount; + text.Insert(cursor, str); + ComputeRendered(); + if (prev_lines != LineCount) + { + Console.SetCursorPosition(0, home_row); + Render(); + cursor += str.Length; + ForceCursor(cursor); + } + else + { + RenderFrom(cursor); + cursor += str.Length; + ForceCursor(cursor); + UpdateHomeRow(TextToScreenPos(cursor)); + } + } + + void SetSearchPrompt(string s) + { + SetPrompt("(reverse-i-search)`" + s + "': "); + } + + void ReverseSearch() + { + int p; + + if (cursor == text.Length) + { + // The cursor is at the end of the string + + p = text.ToString().LastIndexOf(search); + if (p != -1) + { + match_at = p; + cursor = p; + ForceCursor(cursor); + return; + } + } + else + { + // The cursor is somewhere in the middle of the string + int start = (cursor == match_at) ? cursor - 1 : cursor; + if (start != -1) + { + p = text.ToString().LastIndexOf(search, start); + if (p != -1) + { + match_at = p; + cursor = p; + ForceCursor(cursor); + return; + } + } + } + + // Need to search backwards in history + HistoryUpdateLine(); + string s = history.SearchBackward(search); + if (s != null) + { + match_at = -1; + SetText(s); + ReverseSearch(); + } + } + + void CmdReverseSearch() + { + if (searching == 0) + { + match_at = -1; + last_search = search; + searching = -1; + search = ""; + SetSearchPrompt(""); + } + else + { + if (search == "") + { + if (last_search != "" && last_search != null) + { + search = last_search; + SetSearchPrompt(search); + + ReverseSearch(); + } + return; + } + ReverseSearch(); + } + } + + void SearchAppend(char c) + { + search = search + c; + SetSearchPrompt(search); + + // + // If the new typed data still matches the current text, stay here + // + if (cursor < text.Length) + { + string r = text.ToString(cursor, text.Length - cursor); + if (r.StartsWith(search)) + return; + } + + ReverseSearch(); + } + + void CmdRefresh() + { + Console.Clear(); + max_rendered = 0; + Render(); + ForceCursor(cursor); + } + + void InterruptEdit(object sender, ConsoleCancelEventArgs a) + { + // Do not abort our program: + a.Cancel = false; + + // Interrupt the editor + edit_thread.Abort(); + } + + void HandleChar(char c) + { + if (searching != 0) + SearchAppend(c); + else + InsertChar(c); + } + + void EditLoop() + { + ConsoleKeyInfo cki; + + while (!done) + { + ConsoleModifiers mod; + + cki = Console.ReadKey(true); + if (cki.Key == ConsoleKey.Escape) + { + cki = Console.ReadKey(true); + + mod = ConsoleModifiers.Alt; + } + else + mod = cki.Modifiers; + + bool handled = false; + + foreach (Handler handler in handlers) + { + ConsoleKeyInfo t = handler.CKI; + + if (t.Key == cki.Key && t.Modifiers == mod) + { + handled = true; + handler.KeyHandler(); + last_handler = handler.KeyHandler; + break; + } + else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom) + { + handled = true; + handler.KeyHandler(); + last_handler = handler.KeyHandler; + break; + } + } + if (handled) + { + if (searching != 0) + { + if (last_handler != CmdReverseSearch) + { + searching = 0; + SetPrompt(prompt); + } + } + continue; + } + + if (cki.KeyChar != (char)0) + HandleChar(cki.KeyChar); + } + } + + void InitText(string initial) + { + text = new StringBuilder(initial); + ComputeRendered(); + cursor = text.Length; + Render(); + ForceCursor(cursor); + } + + void SetText(string newtext) + { + Console.SetCursorPosition(0, home_row); + InitText(newtext); + } + + void SetPrompt(string newprompt) + { + shown_prompt = newprompt; + Console.SetCursorPosition(0, home_row); + Render(); + ForceCursor(cursor); + } + + public string Edit(string prompt, string initial) + { + edit_thread = Thread.CurrentThread; + searching = 0; + Console.CancelKeyPress += InterruptEdit; + + done = false; + history.CursorToEnd(); + max_rendered = 0; + + Prompt = prompt; + shown_prompt = prompt; + InitText(initial); + history.Append(initial); + + do + { + try + { + EditLoop(); + } + catch (ThreadAbortException) + { + searching = 0; + Thread.ResetAbort(); + Console.WriteLine(); + SetPrompt(prompt); + SetText(""); + } + } while (!done); + Console.WriteLine(); + + Console.CancelKeyPress -= InterruptEdit; + + if (text == null) + { + history.Close(); + return null; + } + + string result = text.ToString(); + if (result != "") + history.Accept(result); + else + history.RemoveLast(); + + return result; + } + + public void SaveHistory() + { + if (history != null) + { + history.Close(); + } + } + + public bool TabAtStartCompletes { get; set; } + + // + // Emulates the bash-like behavior, where edits done to the + // history are recorded + // + class History + { + string[] history; + int head, tail; + int cursor, count; + string histfile; + + public History(string app, int size) + { + if (size < 1) + throw new ArgumentException("size"); + + if (app != null) + { + string dir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + //Console.WriteLine (dir); + if (!Directory.Exists(dir)) + { + try + { + Directory.CreateDirectory(dir); + } + catch + { + app = null; + } + } + if (app != null) + histfile = Path.Combine(dir, app) + ".history"; + } + + history = new string[size]; + head = tail = cursor = 0; + + if (File.Exists(histfile)) + { + using (StreamReader sr = File.OpenText(histfile)) + { + string line; + + while ((line = sr.ReadLine()) != null) + { + if (line != "") + Append(line); + } + } + } + } + + public void Close() + { + if (histfile == null) + return; + + try + { + using (StreamWriter sw = File.CreateText(histfile)) + { + int start = (count == history.Length) ? head : tail; + for (int i = start; i < start + count; i++) + { + int p = i % history.Length; + sw.WriteLine(history[p]); + } + } + } + catch + { + // ignore + } + } + + // + // Appends a value to the history + // + public void Append(string s) + { + //Console.WriteLine ("APPENDING {0} head={1} tail={2}", s, head, tail); + history[head] = s; + head = (head + 1) % history.Length; + if (head == tail) + tail = (tail + 1 % history.Length); + if (count != history.Length) + count++; + //Console.WriteLine ("DONE: head={1} tail={2}", s, head, tail); + } + + // + // Updates the current cursor location with the string, + // to support editing of history items. For the current + // line to participate, an Append must be done before. + // + public void Update(string s) + { + history[cursor] = s; + } + + public void RemoveLast() + { + head = head - 1; + if (head < 0) + head = history.Length - 1; + } + + public void Accept(string s) + { + int t = head - 1; + if (t < 0) + t = history.Length - 1; + + history[t] = s; + } + + public bool PreviousAvailable() + { + //Console.WriteLine ("h={0} t={1} cursor={2}", head, tail, cursor); + if (count == 0) + return false; + int next = cursor - 1; + if (next < 0) + next = count - 1; + + if (next == head) + return false; + + return true; + } + + public bool NextAvailable() + { + if (count == 0) + return false; + int next = (cursor + 1) % history.Length; + if (next == head) + return false; + return true; + } + + + // + // Returns: a string with the previous line contents, or + // nul if there is no data in the history to move to. + // + public string Previous() + { + if (!PreviousAvailable()) + return null; + + cursor--; + if (cursor < 0) + cursor = history.Length - 1; + + return history[cursor]; + } + + public string Next() + { + if (!NextAvailable()) + return null; + + cursor = (cursor + 1) % history.Length; + return history[cursor]; + } + + public void CursorToEnd() + { + if (head == tail) + return; + + cursor = head; + } + + public void Dump() + { + Console.WriteLine("Head={0} Tail={1} Cursor={2} count={3}", head, tail, cursor, count); + for (int i = 0; i < history.Length; i++) + { + Console.WriteLine(" {0} {1}: {2}", i == cursor ? "==>" : " ", i, history[i]); + } + //log.Flush (); + } + + public string SearchBackward(string term) + { + for (int i = 0; i < count; i++) + { + int slot = cursor - i - 1; + if (slot < 0) + slot = history.Length + slot; + if (slot >= history.Length) + slot = 0; + if (history[slot] != null && history[slot].IndexOf(term) != -1) + { + cursor = slot; + return history[slot]; + } + } + + return null; + } + + } + } + +#if DEMO + class Demo { + static void Main () + { + LineEditor le = new LineEditor ("foo"); + string s; + + while ((s = le.Edit ("shell> ", "")) != null){ + Console.WriteLine ("----> [{0}]", s); + } + } + } +#endif +} diff --git a/src/ScriptCs.Hosting/LoggerConfigurator.cs b/src/ScriptCs.Hosting/LoggerConfigurator.cs deleted file mode 100644 index ebc51dc0..00000000 --- a/src/ScriptCs.Hosting/LoggerConfigurator.cs +++ /dev/null @@ -1,35 +0,0 @@ -using ScriptCs.Contracts; -using ScriptCs.Logging; -using LogLevel = ScriptCs.Contracts.LogLevel; - -namespace ScriptCs.Hosting -{ - public class LoggerConfigurator : ILoggerConfigurator - { - private const string LoggerName = "scriptcs"; - - private readonly LogLevel _logLevel; - - private ILog _logger; - - public LoggerConfigurator(LogLevel logLevel) - { - _logLevel = logLevel; - } - - public void Configure(IConsole console) - { - _logger = new ScriptConsoleLogger(_logLevel, console, LogProvider.GetLogger(LoggerName)); - } - - public void Configure(IConsole console, ILog log) - { - _logger = new ScriptConsoleLogger(_logLevel, console, log); - } - - public ILog GetLogger() - { - return _logger; - } - } -} \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ModuleLoader.cs b/src/ScriptCs.Hosting/ModuleLoader.cs index 87918784..0e402b37 100644 --- a/src/ScriptCs.Hosting/ModuleLoader.cs +++ b/src/ScriptCs.Hosting/ModuleLoader.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Hosting { @@ -14,8 +13,7 @@ public class ModuleLoader : IModuleLoader { internal static readonly Dictionary DefaultCSharpModules = new Dictionary { - {"roslyn", "ScriptCs.Engine.Roslyn.dll"}, - {"mono", "ScriptCs.Engine.Mono.dll"} + {"roslyn", "ScriptCs.Engine.Roslyn.dll"} }; internal static readonly string DefaultCSharpExtension = ".csx"; @@ -28,15 +26,17 @@ public class ModuleLoader : IModuleLoader private readonly IAssemblyUtility _assemblyUtility; [ImportingConstructor] - public ModuleLoader(IAssemblyResolver resolver, ILog logger, IFileSystem fileSystem, IAssemblyUtility assemblyUtility) : - this(resolver, logger, null, null, fileSystem, assemblyUtility) + public ModuleLoader(IAssemblyResolver resolver, ILogProvider logProvider, IFileSystem fileSystem, IAssemblyUtility assemblyUtility) : + this(resolver, logProvider, null, null, fileSystem, assemblyUtility) { } - public ModuleLoader(IAssemblyResolver resolver, ILog logger, Action addToCatalog, Func>> getLazyModules, IFileSystem fileSystem, IAssemblyUtility assemblyUtility) + public ModuleLoader(IAssemblyResolver resolver, ILogProvider logProvider, Action addToCatalog, Func>> getLazyModules, IFileSystem fileSystem, IAssemblyUtility assemblyUtility) { + Guard.AgainstNullArgument("logProvider", logProvider); + _resolver = resolver; - _logger = logger; + _logger = logProvider.ForCurrentType(); if (addToCatalog == null) { @@ -49,7 +49,7 @@ public ModuleLoader(IAssemblyResolver resolver, ILog logger, Action().Where(d => d.Name == "$id").ToList(); - if (idProperties.Any()) + var writer = new JTokenWriter(); + var serializer = JsonSerializer.Create(settings); + serializer.Serialize(writer, value); + + var container = writer.Token as JContainer; + if (container != null) { - var refProperties = container.Descendants().OfType().Where(d => d.Name == "$ref").ToList(); - if (refProperties.Any()) + var idProperties = container.Descendants().OfType().Where(d => d.Name == "$id").ToList(); + if (idProperties.Any()) { - foreach (var idProperty in idProperties - .Where(idProperty => refProperties - .All(refProperty => refProperty.Value.ToString() != idProperty.Value.ToString()))) + var refProperties = container.Descendants().OfType().Where(d => d.Name == "$ref").ToList(); + if (refProperties.Any()) { - idProperty.Remove(); + foreach (var idProperty in idProperties + .Where(idProperty => refProperties + .All(refProperty => refProperty.Value.ToString() != idProperty.Value.ToString()))) + { + idProperty.Remove(); + } } - } - else - { - foreach (var idProperty in idProperties) + else { - idProperty.Remove(); + foreach (var idProperty in idProperties) + { + idProperty.Remove(); + } } } } - } - return writer.Token.ToString(); + return writer.Token.ToString(); + } + catch (JsonSerializationException) + { + return string.Format("Couldn't serialize a returned instance of {0}", value.GetType()); + } } } } diff --git a/src/ScriptCs.Hosting/Package/NugetInstallationProvider.cs b/src/ScriptCs.Hosting/Package/NugetInstallationProvider.cs index a1d81ebd..e4eddb98 100644 --- a/src/ScriptCs.Hosting/Package/NugetInstallationProvider.cs +++ b/src/ScriptCs.Hosting/Package/NugetInstallationProvider.cs @@ -4,7 +4,6 @@ using System.Linq; using NuGet; using ScriptCs.Contracts; -using ScriptCs.Logging; using IFileSystem = ScriptCs.Contracts.IFileSystem; namespace ScriptCs.Hosting.Package @@ -18,12 +17,13 @@ public class NugetInstallationProvider : IInstallationProvider private static readonly Version EmptyVersion = new Version(); - public NugetInstallationProvider(IFileSystem fileSystem, ILog logger) + public NugetInstallationProvider(IFileSystem fileSystem, ILogProvider logProvider) { Guard.AgainstNullArgument("fileSystem", fileSystem); + Guard.AgainstNullArgument("logProvider", logProvider); _fileSystem = fileSystem; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public void Initialize() @@ -31,7 +31,7 @@ public void Initialize() var path = Path.Combine(_fileSystem.CurrentDirectory, _fileSystem.PackagesFolder); _repositoryUrls = GetRepositorySources(path); var remoteRepository = new AggregateRepository(PackageRepositoryFactory.Default, _repositoryUrls, true); - _manager = new PackageManager(remoteRepository, path); + _manager = new ScriptCsPackageManager(remoteRepository, path); } public IEnumerable GetRepositorySources(string path) @@ -55,6 +55,9 @@ public IEnumerable GetRepositorySources(string path) } var sourceProvider = new PackageSourceProvider(settings); + + HttpClient.DefaultCredentialProvider = new SettingsCredentialProvider(NullCredentialProvider.Instance, sourceProvider); + var sources = sourceProvider.LoadPackageSources().Where(i => i.IsEnabled == true); if (sources == null || !sources.Any()) @@ -68,9 +71,10 @@ public IEnumerable GetRepositorySources(string path) public void InstallPackage(IPackageReference packageId, bool allowPreRelease = false) { Guard.AgainstNullArgument("packageId", packageId); - + var version = GetVersion(packageId); var packageName = packageId.PackageId + " " + (version == null ? string.Empty : packageId.Version.ToString()); + _manager.InstallPackage(packageId.PackageId, version, allowPrereleaseVersions: allowPreRelease, ignoreDependencies: false); _logger.Info("Installed: " + packageName); } diff --git a/src/ScriptCs.Hosting/Package/NugetMachineWideSettings.cs b/src/ScriptCs.Hosting/Package/NugetMachineWideSettings.cs index 915ce9a8..ffd6d099 100644 --- a/src/ScriptCs.Hosting/Package/NugetMachineWideSettings.cs +++ b/src/ScriptCs.Hosting/Package/NugetMachineWideSettings.cs @@ -14,12 +14,6 @@ public NugetMachineWideSettings() _settings = new Lazy>(() => NuGet.Settings.LoadMachineWideSettings(new PhysicalFileSystem(baseDirectory))); } - public IEnumerable Settings - { - get - { - return _settings.Value; - } - } + public IEnumerable Settings => _settings.Value; } } \ No newline at end of file diff --git a/src/ScriptCs.Hosting/Package/PackageContainer.cs b/src/ScriptCs.Hosting/Package/PackageContainer.cs index c726a738..1dc7c8c8 100644 --- a/src/ScriptCs.Hosting/Package/PackageContainer.cs +++ b/src/ScriptCs.Hosting/Package/PackageContainer.cs @@ -1,10 +1,10 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Versioning; using NuGet; using ScriptCs.Contracts; -using ScriptCs.Logging; using IFileSystem = ScriptCs.Contracts.IFileSystem; namespace ScriptCs.Hosting.Package @@ -19,12 +19,13 @@ public class PackageContainer : IPackageContainer private readonly ILog _logger; - public PackageContainer(IFileSystem fileSystem, ILog logger) + public PackageContainer(IFileSystem fileSystem, ILogProvider logProvider) { Guard.AgainstNullArgument("fileSystem", fileSystem); + Guard.AgainstNullArgument("logProvider", logProvider); _fileSystem = fileSystem; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public void CreatePackageFile() @@ -149,7 +150,7 @@ private static bool IsValidFramework(FrameworkName frameworkName) private static bool IsValidProfile(string profile) { - return profile == "net40" || profile == "net45"; + return profile == "net40" || profile == "net45" || profile == "net461" || profile == "netstandard20"; } } } \ No newline at end of file diff --git a/src/ScriptCs.Hosting/Package/PackageInstaller.cs b/src/ScriptCs.Hosting/Package/PackageInstaller.cs index 994b77e1..45c20d31 100644 --- a/src/ScriptCs.Hosting/Package/PackageInstaller.cs +++ b/src/ScriptCs.Hosting/Package/PackageInstaller.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Hosting.Package { @@ -11,13 +10,13 @@ public class PackageInstaller : IPackageInstaller private readonly IInstallationProvider _installer; private readonly ILog _logger; - public PackageInstaller(IInstallationProvider installer, ILog logger) + public PackageInstaller(IInstallationProvider installer, ILogProvider logProvider) { Guard.AgainstNullArgument("installer", installer); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); _installer = installer; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public void InstallPackages(IEnumerable packageIds, bool allowPreRelease = false) diff --git a/src/ScriptCs.Hosting/Package/PackageObject.cs b/src/ScriptCs.Hosting/Package/PackageObject.cs index ab3ad171..e997d3f2 100644 --- a/src/ScriptCs.Hosting/Package/PackageObject.cs +++ b/src/ScriptCs.Hosting/Package/PackageObject.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.Versioning; +using System.Text.RegularExpressions; using NuGet; using ScriptCs.Contracts; @@ -49,10 +51,7 @@ public PackageObject(string packageId) public IEnumerable Dependencies { get; set; } - public string FullName - { - get { return Id + "." + TextVersion; } - } + public string FullName => Id + "." + TextVersion; public IEnumerable GetCompatibleDlls(FrameworkName frameworkName) { @@ -60,6 +59,28 @@ public IEnumerable GetCompatibleDlls(FrameworkName frameworkName) IEnumerable compatibleFiles; VersionUtility.TryGetCompatibleItems(frameworkName, dlls, out compatibleFiles); + // HACK: Delete unnecessary temporary NuGet files + List tempDirectories = new List(); + foreach (PhysicalPackageFile file in dlls) + { + var match = Regex.Match(((PhysicalPackageFile)dlls.First()).SourcePath, "(" + Path.Combine(Path.GetTempPath(), "nuget").Replace("\\", "\\\\") + "\\\\[^\\\\]+?)\\\\"); + if (match.Success && match.Groups.Count > 1) + { + tempDirectories.Add(match.Groups[1].Value); + } + + } + + foreach (string directory in tempDirectories.Distinct()) + { + if (Directory.Exists(directory)) + { + Directory.Delete(directory, true); + } + } + + // /HACK + return compatibleFiles != null ? compatibleFiles.Select(i => i.Path) : null; } diff --git a/src/ScriptCs.Hosting/Package/ScriptCsPackageManager.cs b/src/ScriptCs.Hosting/Package/ScriptCsPackageManager.cs new file mode 100644 index 00000000..d060c5d6 --- /dev/null +++ b/src/ScriptCs.Hosting/Package/ScriptCsPackageManager.cs @@ -0,0 +1,26 @@ +using System.Linq; +using System.Reflection; +using System.Runtime.Versioning; +using NuGet; + +namespace ScriptCs.Hosting.Package +{ + //needed to allow forcing framework version for installation as the InstallPackage method that accepts version is protected and all other overloads force version to null! + public class ScriptCsPackageManager : PackageManager + { + public ScriptCsPackageManager(IPackageRepository sourceRepository, string path) : base(sourceRepository, path) + { + } + + public override void InstallPackage(IPackage package, bool ignoreDependencies, bool allowPrereleaseVersions) + { + base.InstallPackage(package, new FrameworkName(FrameworkUtils.FrameworkName), ignoreDependencies, allowPrereleaseVersions); + } + + public override void InstallPackage(string packageId, SemanticVersion version, bool ignoreDependencies, bool allowPrereleaseVersions) + { + var package = PackageRepositoryHelper.ResolvePackage(SourceRepository, LocalRepository, packageId, version, allowPrereleaseVersions); + base.InstallPackage(package, new FrameworkName(FrameworkUtils.FrameworkName), ignoreDependencies, allowPrereleaseVersions); + } + } +} \ No newline at end of file diff --git a/src/ScriptCs.Hosting/Properties/AssemblyInfo.cs b/src/ScriptCs.Hosting/Properties/AssemblyInfo.cs index 9aba106c..ab73017e 100644 --- a/src/ScriptCs.Hosting/Properties/AssemblyInfo.cs +++ b/src/ScriptCs.Hosting/Properties/AssemblyInfo.cs @@ -1,7 +1,4 @@ using System.Reflection; using System.Runtime.CompilerServices; -[assembly: AssemblyTitle("ScriptCs.Hosting")] -[assembly: AssemblyDescription("ScriptCs.Hosting provides common services necessary for hosting scriptcs in your application.")] - [assembly: InternalsVisibleTo("ScriptCs.Hosting.Tests")] \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ReplCommands/OpenVSCommand.cs b/src/ScriptCs.Hosting/ReplCommands/OpenVSCommand.cs new file mode 100644 index 00000000..17fe4193 --- /dev/null +++ b/src/ScriptCs.Hosting/ReplCommands/OpenVSCommand.cs @@ -0,0 +1,43 @@ +using ScriptCs.Contracts; +using System; + +namespace ScriptCs.Hosting.ReplCommands +{ + public class OpenVsCommand : IReplCommand + { + private readonly IConsole _console; + private IVisualStudioSolutionWriter _writer; + + public OpenVsCommand(IConsole console, IVisualStudioSolutionWriter writer) + { + _console = console; + _writer = writer; + } + + public object Execute(IRepl repl, object[] args) + { + if (PlatformID != PlatformID.Win32NT) + { + _console.WriteLine("Requires Windows 8 or later to run"); + return null; + } + var fs = repl.FileSystem; + string arg = args.Length > 0 ? (string)args[0] : null; + var launcher = _writer.WriteSolution(fs, arg, new VisualStudioSolution()); + _console.WriteLine("Opening Visual Studio"); + LaunchSolution(launcher); + return null; + } + + protected internal virtual void LaunchSolution(string launcher) + { + System.Diagnostics.Process.Start(launcher); + } + + protected internal virtual PlatformID PlatformID => Environment.OSVersion.Platform; + + public string Description => "Opens a script to edit/debug in Visual Studio"; + + public string CommandName => "openvs"; + } +} diff --git a/src/ScriptCs.Hosting/RuntimeServices.cs b/src/ScriptCs.Hosting/RuntimeServices.cs index 4b7ef65d..9a07390c 100644 --- a/src/ScriptCs.Hosting/RuntimeServices.cs +++ b/src/ScriptCs.Hosting/RuntimeServices.cs @@ -7,12 +7,12 @@ using Autofac.Integration.Mef; using ScriptCs.Contracts; using ScriptCs.Hosting.Package; -using ScriptCs.Logging; namespace ScriptCs.Hosting { public class RuntimeServices : ScriptServicesRegistration, IRuntimeServices { + private readonly ILog _log; private readonly IConsole _console; private readonly Type _scriptEngineType; private readonly Type _scriptExecutorType; @@ -22,7 +22,7 @@ public class RuntimeServices : ScriptServicesRegistration, IRuntimeServices private readonly string _scriptName; public RuntimeServices( - ILog logger, + ILogProvider logProvider, IDictionary overrides, IConsole console, Type scriptEngineType, @@ -31,8 +31,11 @@ public RuntimeServices( bool initDirectoryCatalog, IInitializationServices initializationServices, string scriptName) - : base(logger, overrides) + : base(logProvider, overrides) { + Guard.AgainstNullArgument("logProvider", logProvider); + + _log = logProvider.ForCurrentType(); _console = console; _scriptEngineType = scriptEngineType; _scriptExecutorType = scriptExecutorType; @@ -42,22 +45,19 @@ public RuntimeServices( _scriptName = scriptName; } - internal bool InitDirectoryCatalog - { - get { return _initDirectoryCatalog; } - } + internal bool InitDirectoryCatalog => _initDirectoryCatalog; protected override IContainer CreateContainer() { var builder = new ContainerBuilder(); - this.Logger.Debug("Registering runtime services"); + _log.Debug("Registering runtime services"); - builder.RegisterInstance(this.Logger).Exported(x => x.As()); + builder.RegisterInstance(this.LogProvider).Exported(x => x.As()); builder.RegisterType(_scriptEngineType).As().SingleInstance(); builder.RegisterType(_scriptExecutorType).As().SingleInstance(); - builder.RegisterType(_replType).As().SingleInstance(); + builder.RegisterType(_replType ?? typeof(Repl)).As().SingleInstance(); builder.RegisterType().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().SingleInstance(); RegisterLineProcessors(builder); RegisterReplCommands(builder); @@ -101,12 +101,14 @@ protected override IContainer CreateContainer() RegisterOverrideOrDefault( builder, b => b.RegisterInstance(_console)); - RegisterOverrideOrDefault( - builder, b => b.RegisterType().As().SingleInstance()); - RegisterOverrideOrDefault( builder, b => b.RegisterType().As().SingleInstance()); + RegisterOverrideOrDefault( + builder, b => b.RegisterType().As().SingleInstance()); + + RegisterOverrideOrDefault(builder, b => b.RegisterType().As().SingleInstance()); + if (_initDirectoryCatalog) { var fileSystem = _initializationServices.GetFileSystem(); @@ -134,7 +136,7 @@ protected override IContainer CreateContainer() { foreach (var ex in typeLoadEx.LoaderExceptions.GroupBy(x => x.Message)) { - Logger.DebugFormat( + _log.DebugFormat( "Failure loading assembly: {0}. Exception: {1}", assemblyPath, ex.First().Message); } } @@ -142,12 +144,12 @@ protected override IContainer CreateContainer() catch (Exception ex) { assemblyLoadFailures = true; - Logger.DebugFormat("Failure loading assembly: {0}. Exception: {1}", assemblyPath, ex.Message); + _log.DebugFormat("Failure loading assembly: {0}. Exception: {1}", assemblyPath, ex.Message); } } if (assemblyLoadFailures) { - Logger.Warn(string.IsNullOrEmpty(_scriptName) + _log.Warn(string.IsNullOrEmpty(_scriptName) ? "Some assemblies failed to load. Launch with '-repl -loglevel debug' to see the details" : "Some assemblies failed to load. Launch with '-loglevel debug' to see the details"); } @@ -176,8 +178,8 @@ private static void RegisterReplCommands(ContainerBuilder builder) public ScriptServices GetScriptServices() { - this.Logger.Debug("Resolving ScriptServices"); + _log.Debug("Resolving ScriptServices"); return Container.Resolve(); } } -} +} \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ScriptConsole.cs b/src/ScriptCs.Hosting/ScriptConsole.cs index 706a014c..251e720e 100644 --- a/src/ScriptCs.Hosting/ScriptConsole.cs +++ b/src/ScriptCs.Hosting/ScriptConsole.cs @@ -1,13 +1,16 @@ using System; using ScriptCs.Contracts; +using Mono.Terminal; namespace ScriptCs.Hosting { public class ScriptConsole : IConsole { + private LineEditor _editor; + public ScriptConsole() { - Console.CancelKeyPress += HandleCancelKeyPress; + _editor = new LineEditor ("scriptcs"); } public void Write(string value) @@ -25,9 +28,9 @@ public void WriteLine(string value) Console.WriteLine(value); } - public string ReadLine() + public string ReadLine(string prompt) { - return Console.ReadLine(); + return _editor.Edit (prompt, ""); } public void Clear() @@ -38,7 +41,6 @@ public void Clear() public void Exit() { ResetColor(); - Console.CancelKeyPress -= HandleCancelKeyPress; Environment.Exit(0); } @@ -47,15 +49,12 @@ public void ResetColor() Console.ResetColor(); } - private void HandleCancelKeyPress(object sender, ConsoleCancelEventArgs e) - { - ResetColor(); - } - public ConsoleColor ForegroundColor { - get { return Console.ForegroundColor; } - set { Console.ForegroundColor = value; } + get => Console.ForegroundColor; + set => Console.ForegroundColor = value; } + + public int Width => Console.BufferWidth; } } \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ScriptConsoleLogger.cs b/src/ScriptCs.Hosting/ScriptConsoleLogger.cs deleted file mode 100644 index e194d38b..00000000 --- a/src/ScriptCs.Hosting/ScriptConsoleLogger.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using ScriptCs.Contracts; -using ScriptCs.Logging; -using LogLevel = ScriptCs.Contracts.LogLevel; - -namespace ScriptCs.Hosting -{ - public class ScriptConsoleLogger : ILog - { - private readonly LogLevel _consoleLogLevel; - private readonly IConsole _console; - private readonly ILog _log; - private readonly Dictionary _colors = - new Dictionary - { - { Logging.LogLevel.Fatal, ConsoleColor.Red }, - { Logging.LogLevel.Error, ConsoleColor.DarkRed }, - { Logging.LogLevel.Warn, ConsoleColor.DarkYellow }, - { Logging.LogLevel.Info, ConsoleColor.Gray }, - { Logging.LogLevel.Debug, ConsoleColor.DarkGray }, - { Logging.LogLevel.Trace, ConsoleColor.DarkMagenta }, - }; - - public ScriptConsoleLogger(LogLevel consoleLogLevel, IConsole console, ILog log) - { - Guard.AgainstNullArgument("console", console); - Guard.AgainstNullArgument("log", log); - - _consoleLogLevel = consoleLogLevel; - _console = console; - _log = log; - } - - public bool Log( - Logging.LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - var logged = _log.Log(logLevel, messageFunc, exception, formatParameters); - var consoleLog = false; - switch (logLevel) - { - case Logging.LogLevel.Fatal: - consoleLog = true; - break; - case Logging.LogLevel.Error: - consoleLog = true; - break; - case Logging.LogLevel.Warn: - consoleLog = true; - break; - case Logging.LogLevel.Info: - consoleLog = _consoleLogLevel != LogLevel.Error; - break; - case Logging.LogLevel.Debug: - consoleLog = _consoleLogLevel == LogLevel.Debug || _consoleLogLevel == LogLevel.Trace; - break; - case Logging.LogLevel.Trace: - consoleLog = _consoleLogLevel == LogLevel.Trace; - break; - } - - if (consoleLog && messageFunc != null) - { - var prefix = logLevel == Logging.LogLevel.Info - ? null - : string.Concat(logLevel.ToString().ToUpperInvariant(), ": "); - - var message = string.Format(CultureInfo.InvariantCulture, messageFunc(), formatParameters); - - var suffix = string.Empty; - if (exception != null) - { - if (_consoleLogLevel == LogLevel.Debug || _consoleLogLevel == LogLevel.Trace) - { - var exceptions = new List(); - while (exception != null) - { - var exceptionString = string.Format( - CultureInfo.InvariantCulture, - "[{0}] {1}{2}{3}", - exception.GetType().FullName, - exception.Message, - Environment.NewLine, - exception.StackTrace); - - exceptions.Add(exceptionString); - exception = exception.InnerException; - } - - var divider = string.Format( - CultureInfo.InvariantCulture, "{0}{1}{0}", Environment.NewLine, "=== INNER EXCEPTION ==="); - - suffix = " " + string.Join(divider, exceptions); - } - else - { - suffix = string.Format( - CultureInfo.InvariantCulture, " [{0}] {1}", exception.GetType().Name, exception.Message); - } - } - - ConsoleColor color; - if (!_colors.TryGetValue(logLevel, out color)) - { - color = ConsoleColor.White; - } - - var originalColor = _console.ForegroundColor; - _console.ForegroundColor = color; - try - { - _console.WriteLine(string.Concat(prefix, message, suffix)); - } - finally - { - _console.ForegroundColor = originalColor; - } - } - - return logged || consoleLog; - } - } -} diff --git a/src/ScriptCs.Hosting/ScriptCs.Hosting.csproj b/src/ScriptCs.Hosting/ScriptCs.Hosting.csproj index 99f17b09..3a164047 100644 --- a/src/ScriptCs.Hosting/ScriptCs.Hosting.csproj +++ b/src/ScriptCs.Hosting/ScriptCs.Hosting.csproj @@ -1,123 +1,31 @@ - - - + - Debug - AnyCPU - {9AEF2D95-87FB-4829-B384-34BFE076D531} - Library - ScriptCs.Hosting - ScriptCs.Hosting - Properties - 512 - v4.5 - - ..\..\ - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset + 1.0.0 + net461 + ScriptCs.Hosting + Glenn Block, Filip Wojcieszyn, Justin Rusbatch + https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md + http://scriptcs.net + http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 + ScriptCs.Hosting + ScriptCs.Hosting provides common services necessary for hosting scriptcs in your application. + roslyn csx script scriptcs - - False - ..\..\packages\Autofac.3.3.1\lib\net40\Autofac.dll - - - False - ..\..\packages\Autofac.Mef.3.0.3\lib\net40\Autofac.Integration.Mef.dll - - - ..\..\packages\Microsoft.Web.Xdt.2.1.0\lib\net40\Microsoft.Web.XmlTransform.dll - - - ..\..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll - - - False - ..\..\packages\Nuget.Core.2.8.5\lib\net40-Client\NuGet.Core.dll - - - - - - - - - + - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - Guard.cs - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/ScriptCs.Hosting/ScriptServicesBuilder.cs b/src/ScriptCs.Hosting/ScriptServicesBuilder.cs index 82b93ba6..cd95d4f2 100644 --- a/src/ScriptCs.Hosting/ScriptServicesBuilder.cs +++ b/src/ScriptCs.Hosting/ScriptServicesBuilder.cs @@ -2,15 +2,12 @@ using System.Linq; using System.Collections.Generic; using ScriptCs.Contracts; -using ScriptCs.Logging; -using LogLevel = ScriptCs.Contracts.LogLevel; namespace ScriptCs.Hosting { public class ScriptServicesBuilder : ServiceOverrides, IScriptServicesBuilder { - private readonly ITypeResolver _typeResolver; - private readonly ILog _logger; + private readonly ILogProvider _logProvider; private IRuntimeServices _runtimeServices; private bool _repl; @@ -25,17 +22,14 @@ public class ScriptServicesBuilder : ServiceOverrides, I public ScriptServicesBuilder( IConsole console, - ILog logger, + ILogProvider logProvider, IRuntimeServices runtimeServices = null, - ITypeResolver typeResolver = null, IInitializationServices initializationServices = null) { - InitializationServices = initializationServices ?? new InitializationServices(logger); + InitializationServices = initializationServices ?? new InitializationServices(logProvider); _runtimeServices = runtimeServices; - _typeResolver = typeResolver; - _typeResolver = typeResolver ?? new TypeResolver(); ConsoleInstance = console; - _logger = logger; + _logProvider = logProvider; } public ScriptServices Build() @@ -64,7 +58,7 @@ public ScriptServices Build() if (_runtimeServices == null) { _runtimeServices = new RuntimeServices( - _logger, + _logProvider, Overrides, ConsoleInstance, _scriptEngineType, @@ -80,11 +74,7 @@ public ScriptServices Build() public IScriptServicesBuilder LoadModules(string extension, params string[] moduleNames) { - var engineModule = _typeResolver.ResolveType("Mono.Runtime") != null || moduleNames.Contains("mono") - ? "mono" - : "roslyn"; - - moduleNames = moduleNames.Union(new[] { engineModule }).ToArray(); + moduleNames = moduleNames.Union(new[] { "roslyn" }).ToArray(); var config = new ModuleConfiguration(_cache, _scriptName, _repl, _logLevel, _debug, Overrides); var loader = InitializationServices.GetModuleLoader(); @@ -148,9 +138,6 @@ public IScriptServicesBuilder SetOverride() where TImpl : TCon public IConsole ConsoleInstance { get; private set; } - internal IRuntimeServices RuntimeServices - { - get { return _runtimeServices; } - } + internal IRuntimeServices RuntimeServices => _runtimeServices; } } \ No newline at end of file diff --git a/src/ScriptCs.Hosting/ScriptServicesRegistration.cs b/src/ScriptCs.Hosting/ScriptServicesRegistration.cs index 50c8fb5f..33d03631 100644 --- a/src/ScriptCs.Hosting/ScriptServicesRegistration.cs +++ b/src/ScriptCs.Hosting/ScriptServicesRegistration.cs @@ -3,20 +3,24 @@ using System.Linq; using ScriptCs.Contracts; using Autofac; -using ScriptCs.Logging; namespace ScriptCs.Hosting { public abstract class ScriptServicesRegistration { + private readonly ILogProvider _logProvider; + private readonly ILog _log; private readonly IDictionary _overrides; - public ILog Logger { get; private set; } + public ILogProvider LogProvider => _logProvider; - protected ScriptServicesRegistration(ILog logger, IDictionary overrides) + protected ScriptServicesRegistration(ILogProvider logProvider, IDictionary overrides) { + Guard.AgainstNullArgument("logProvider", logProvider); + _overrides = overrides ?? new Dictionary(); - Logger = logger; + _logProvider = logProvider; + _log = _logProvider.ForCurrentType(); } protected void RegisterOverrideOrDefault(ContainerBuilder builder, Action registrationAction) @@ -26,7 +30,7 @@ protected void RegisterOverrideOrDefault(ContainerBuilder builder, Action(ContainerBuilder builder, Action _container ?? (_container = CreateContainer()); - protected IDictionary Overrides - { - get { return _overrides; } - } + protected IDictionary Overrides => _overrides; protected abstract IContainer CreateContainer(); } diff --git a/src/ScriptCs.Hosting/TypeResolver.cs b/src/ScriptCs.Hosting/TypeResolver.cs index d202b1a1..36f7c35e 100644 --- a/src/ScriptCs.Hosting/TypeResolver.cs +++ b/src/ScriptCs.Hosting/TypeResolver.cs @@ -4,9 +4,6 @@ namespace ScriptCs.Hosting { public class TypeResolver : ITypeResolver { - public Type ResolveType(string type) - { - return Type.GetType(type); - } + public Type ResolveType(string type) => Type.GetType(type); } } \ No newline at end of file diff --git a/src/ScriptCs.Hosting/VisualStudioSolution.cs b/src/ScriptCs.Hosting/VisualStudioSolution.cs new file mode 100644 index 00000000..855f47c6 --- /dev/null +++ b/src/ScriptCs.Hosting/VisualStudioSolution.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ScriptCs.Contracts; + +namespace ScriptCs.Hosting +{ + public class VisualStudioSolution : IVisualStudioSolution + { + internal StringBuilder Header { get; set; } + internal StringBuilder Projects { get; set; } + internal StringBuilder Global { get; set; } + + public VisualStudioSolution() + { + Header = new StringBuilder(); + Projects = new StringBuilder(); + Global = new StringBuilder(); + AddHeader(); + } + + public void AddHeader() + { + Header.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + Header.AppendLine("# Visual Studio 2013"); + Header.AppendLine("VisualStudioVersion = 12.0.30501.0"); + Header.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); + } + + public void AddScriptcsProject(string scriptcsPath, string workingPath, string args, bool attach, Guid projectGuid) + { + Projects.AppendFormat(@"Project(""{{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}}"") = ""scriptcs"", ""{0}"", ""{{{1}}}""{2}", scriptcsPath, projectGuid, Environment.NewLine); + Projects.AppendLine("\tProjectSection(DebuggerProjectSystem) = preProject"); + Projects.AppendLine("\t\tPortSupplier = 00000000-0000-0000-0000-000000000000"); + Projects.AppendFormat("\t\tExecutable = {0}{1}", scriptcsPath, Environment.NewLine); + Projects.AppendLine("\t\tRemoteMachine = localhost"); + Projects.AppendFormat("\t\tStartingDirectory = {0}{1}", workingPath, Environment.NewLine); + Projects.AppendFormat("\t\tArguments = {0}{1}", args, Environment.NewLine); + Projects.AppendLine("\t\tEnvironment = Default"); + Projects.AppendLine("\t\tLaunchingEngine = 00000000-0000-0000-0000-000000000000"); + Projects.AppendLine("\t\tUseLegacyDebugEngines = No"); + Projects.AppendLine("\t\tLaunchSQLEngine = No"); + Projects.AppendFormat("\t\tAttachLaunchAction = {0}{1}", attach == true ? "Yes" : "No", Environment.NewLine); + Projects.AppendLine("\tEndProjectSection"); + Projects.AppendLine("EndProject"); + } + + public void AddProject(string path, string name, Guid guid, string[] files) + { + Projects.AppendFormat(@"Project(""{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}"") = ""{0}"", ""{0}"", ""{{{1}}}""{2}", name, guid, Environment.NewLine); + Projects.AppendLine("\tProjectSection(SolutionItems) = preProject"); + foreach (var file in files) + { + if (path == null) + { + Projects.AppendFormat("\t\t{0} = {0}{1}", file, Environment.NewLine); + } + else + { + Projects.AppendFormat("\t\t{0}\\{1} = {0}\\{1}{2}", path, file, Environment.NewLine); + } + } + Projects.AppendLine("\tEndProjectSection"); + Projects.AppendLine("EndProject"); + } + + public void AddGlobalHeader(Guid projectGuid) + { + Global.AppendLine("Global"); + Global.AppendLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + Global.AppendLine("\t\tDebug|Any CPU = Debug|Any CPU"); + Global.AppendLine("\tEndGlobalSection"); + Global.AppendLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + Global.AppendFormat("\t\t{{{0}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU{1}", projectGuid, Environment.NewLine); + Global.AppendLine("\tEndGlobalSection"); + Global.AppendLine("\tGlobalSection(SolutionProperties) = preSolution"); + Global.AppendLine("\t\tHideSolutionNode = FALSE"); + Global.AppendLine("\tEndGlobalSection"); + } + + public void AddGlobalNestedProjects(IList nestedItems) + { + if (nestedItems.Any()) + { + Global.AppendLine("\tGlobalSection(NestedProjects) = preSolution"); + foreach (var item in nestedItems) + { + Global.AppendFormat("\t\t{{{0}}} = {{{1}}}{2}", item.Project, item.Parent, Environment.NewLine); + } + Global.AppendLine("\tEndGlobalSection"); + } + } + + public void AddGlobal(Guid projectGuid, IList nestedItems) + { + AddGlobalHeader(projectGuid); + AddGlobalNestedProjects(nestedItems); + Global.AppendLine("EndGlobal"); + } + + public override string ToString() + { + var solutionBuilder = new StringBuilder(); + solutionBuilder.Append(Header); + solutionBuilder.Append(Projects); + solutionBuilder.Append(Global); + return solutionBuilder.ToString(); + } + } +} diff --git a/src/ScriptCs.Hosting/VisualStudioSolutionWriter.cs b/src/ScriptCs.Hosting/VisualStudioSolutionWriter.cs new file mode 100644 index 00000000..244f46e4 --- /dev/null +++ b/src/ScriptCs.Hosting/VisualStudioSolutionWriter.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ScriptCs.Contracts; + +namespace ScriptCs.Hosting +{ + public class VisualStudioSolutionWriter : IVisualStudioSolutionWriter + { + internal DirectoryInfo Root { get; set; } + private Guid _nullGuid = new Guid(); + + public string WriteSolution(IFileSystem fs, string script, IVisualStudioSolution solution, IList nestedItems = null) + { + if (nestedItems == null) + { + nestedItems = new List(); + } + + var launcher = Path.Combine(fs.TempPath, "launcher-" + Guid.NewGuid().ToString() + ".sln"); + + if (fs.FileExists(launcher)) + { + fs.FileDelete(launcher); + } + + var currentDir = fs.CurrentDirectory; + var scriptcsPath = Path.Combine(fs.HostBin, "scriptcs.exe"); + var scriptcsArgs = string.Format("{0} -debug -loglevel info", script); + Root = new DirectoryInfo { Name = "Solution Items", FullPath = currentDir}; + var projectGuid = Guid.NewGuid(); + + solution.AddScriptcsProject(scriptcsPath, currentDir, scriptcsArgs, false, projectGuid); + GetDirectoryInfo(fs, currentDir, Root); + AddDirectoryProject(solution, fs, Root, _nullGuid, nestedItems); + solution.AddGlobal(projectGuid, nestedItems); + fs.WriteToFile(launcher, solution.ToString()); + return launcher; + } + + private void AddDirectoryProject(IVisualStudioSolution solution, IFileSystem fs, DirectoryInfo currentDirectory, Guid parent, IList nestedItems) + { + solution.AddProject(currentDirectory.FullPath, currentDirectory.Name, currentDirectory.Guid, currentDirectory.Files.ToArray()); + foreach (DirectoryInfo dir in currentDirectory.Directories.Values) + { + AddDirectoryProject(solution, fs, dir, currentDirectory.Guid, nestedItems); + } + if (parent != _nullGuid) + { + nestedItems.Add(new ProjectItem(currentDirectory.Guid, parent)); + } + } + + private void GetDirectoryInfo(IFileSystem fs, string currentDir, DirectoryInfo root) + { + IEnumerable files; + var packagesFolder = Path.Combine(currentDir, fs.PackagesFolder); + files = fs.EnumerateFilesAndDirectories(currentDir, "*.csx").Where( + f => f.StartsWith(packagesFolder).Equals(false)); + + var skip = currentDir.Length; + + foreach (var file in files) + { + var pruned = file.Substring(skip + 1); + var segments = pruned.Split(Path.DirectorySeparatorChar); + if (segments.Length == 1) + { + root.Files.Add(segments[0]); + } + else + { + var currentDirectory = root; + var path = ""; + for (int i = 0; i < segments.Length - 1; i++) + { + var segment = segments[i]; + path = path + segment + @"\"; + if (!currentDirectory.Directories.ContainsKey(segment)) + { + var newDirectory = new DirectoryInfo { Name = segment, Path=path, FullPath = Path.GetDirectoryName(file)}; + currentDirectory.Directories[segment] = newDirectory; + currentDirectory = newDirectory; + } + else + { + currentDirectory = currentDirectory.Directories[segment]; + } + } + currentDirectory.Files.Add(segments[segments.Length - 1]); + } + } + } + } +} diff --git a/src/ScriptCs.Hosting/app.config b/src/ScriptCs.Hosting/app.config deleted file mode 100644 index ccd457b4..00000000 --- a/src/ScriptCs.Hosting/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/ScriptCs.Hosting/packages.config b/src/ScriptCs.Hosting/packages.config deleted file mode 100644 index ad5778b8..00000000 --- a/src/ScriptCs.Hosting/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/ScriptCs/Command/CleanCommand.cs b/src/ScriptCs/Command/CleanCommand.cs index d3b56bec..f543c595 100644 --- a/src/ScriptCs/Command/CleanCommand.cs +++ b/src/ScriptCs/Command/CleanCommand.cs @@ -1,7 +1,6 @@ using System; using System.IO; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Command { @@ -11,16 +10,16 @@ internal class CleanCommand : ICleanCommand private readonly IFileSystem _fileSystem; private readonly ILog _logger; - public CleanCommand(string scriptName, IFileSystem fileSystem, ILog logger) + public CleanCommand(string scriptName, IFileSystem fileSystem, ILogProvider logProvider) { Guard.AgainstNullArgumentProperty("fileSystem", "PackagesFolder", fileSystem.PackagesFolder); Guard.AgainstNullArgumentProperty("fileSystem", "DllCacheFolder", fileSystem.DllCacheFolder); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); _scriptName = scriptName; _fileSystem = fileSystem; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public CommandResult Execute() diff --git a/src/ScriptCs/Command/CommandFactory.cs b/src/ScriptCs/Command/CommandFactory.cs index 2d4d647b..ae1ef9e1 100644 --- a/src/ScriptCs/Command/CommandFactory.cs +++ b/src/ScriptCs/Command/CommandFactory.cs @@ -38,11 +38,6 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) var scriptServices = _scriptServicesBuilder.Build(); - // HACK (Adam): This should not be the responsbility of the command factory - // but now is not the time to fix this. - // This should be addressed by a wider refactoring, i.e. https://github.com/scriptcs/scriptcs/issues/897 - scriptServices.FileSystemMigrator.Migrate(); - if (config.Global) { var currentDir = _fileSystem.GlobalFolder; @@ -64,15 +59,29 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) scriptServices.FileSystem, scriptServices.ScriptPackResolver, scriptServices.Repl, - scriptServices.Logger, + scriptServices.LogProvider, scriptServices.Console, scriptServices.AssemblyResolver, - scriptServices.FileSystemMigrator, scriptServices.ScriptLibraryComposer); return explicitReplCommand; } + if (config.Eval != null) + { + var executeLooseScriptCommand = new ExecuteLooseScriptCommand( + config.Eval, + scriptArgs, + scriptServices.FileSystem, + scriptServices.Executor, + scriptServices.ScriptPackResolver, + scriptServices.LogProvider, + scriptServices.AssemblyResolver, + scriptServices.ScriptLibraryComposer); + + return executeLooseScriptCommand; + } + if (config.ScriptName != null) { var currentDirectory = _fileSystem.CurrentDirectory; @@ -89,7 +98,7 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) _initializationServices.GetPackageAssemblyResolver(), _initializationServices.GetPackageInstaller(), scriptServices.ScriptLibraryComposer, - _initializationServices.Logger); + _initializationServices.LogProvider); var executeCommand = new DeferredCreationCommand(() => CreateScriptCommand( @@ -108,7 +117,7 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) var saveCommand = new SaveCommand( _initializationServices.GetPackageAssemblyResolver(), _fileSystem, - _initializationServices.Logger); + _initializationServices.LogProvider); if (config.Global) { @@ -120,7 +129,7 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) } } - var cleanCommand = new CleanCommand(config.ScriptName, _fileSystem, _initializationServices.Logger); + var cleanCommand = new CleanCommand(config.ScriptName, _fileSystem, _initializationServices.LogProvider); return new CompositeCommand(saveCommand, cleanCommand); } @@ -130,7 +139,7 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) return new SaveCommand( _initializationServices.GetPackageAssemblyResolver(), _fileSystem, - _initializationServices.Logger); + _initializationServices.LogProvider); } if (config.PackageName != null) @@ -145,9 +154,9 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) packageAssemblyResolver, _initializationServices.GetPackageInstaller(), scriptServices.ScriptLibraryComposer, - _initializationServices.Logger); + _initializationServices.LogProvider); - var saveCommand = new SaveCommand(packageAssemblyResolver, _fileSystem, _initializationServices.Logger); + var saveCommand = new SaveCommand(packageAssemblyResolver, _fileSystem, _initializationServices.LogProvider); return new CompositeCommand(installCommand, saveCommand); } @@ -159,10 +168,9 @@ public ICommand CreateCommand(Config config, string[] scriptArgs) scriptServices.FileSystem, scriptServices.ScriptPackResolver, scriptServices.Repl, - scriptServices.Logger, + scriptServices.LogProvider, scriptServices.Console, scriptServices.AssemblyResolver, - scriptServices.FileSystemMigrator, scriptServices.ScriptLibraryComposer); return replCommand; @@ -177,17 +185,15 @@ private static IScriptCommand CreateScriptCommand( scriptArgs, scriptServices.Console, scriptServices.FileSystem, - scriptServices.Logger, - scriptServices.FileSystemMigrator) + scriptServices.LogProvider) : new ExecuteScriptCommand( config.ScriptName, scriptArgs, scriptServices.FileSystem, scriptServices.Executor, scriptServices.ScriptPackResolver, - scriptServices.Logger, + scriptServices.LogProvider, scriptServices.AssemblyResolver, - scriptServices.FileSystemMigrator, scriptServices.ScriptLibraryComposer); } } diff --git a/src/ScriptCs/Command/CrossAppDomainExecuteScriptCommand.cs b/src/ScriptCs/Command/CrossAppDomainExecuteScriptCommand.cs index c6a5c562..b4ac5dca 100644 --- a/src/ScriptCs/Command/CrossAppDomainExecuteScriptCommand.cs +++ b/src/ScriptCs/Command/CrossAppDomainExecuteScriptCommand.cs @@ -23,9 +23,8 @@ public void Execute() services.FileSystem, services.Executor, services.ScriptPackResolver, - services.Logger, + services.LogProvider, services.AssemblyResolver, - services.FileSystemMigrator, services.ScriptLibraryComposer); this.Result = command.Execute(); diff --git a/src/ScriptCs/Command/DeferredCreationCommand.cs b/src/ScriptCs/Command/DeferredCreationCommand.cs index 50b9ced6..af3953e2 100644 --- a/src/ScriptCs/Command/DeferredCreationCommand.cs +++ b/src/ScriptCs/Command/DeferredCreationCommand.cs @@ -13,9 +13,6 @@ public DeferredCreationCommand(Func factory) _factory = factory; } - public CommandResult Execute() - { - return _factory().Execute(); - } + public CommandResult Execute() => _factory().Execute(); } } \ No newline at end of file diff --git a/src/ScriptCs/Command/ExecuteLooseScriptCommand.cs b/src/ScriptCs/Command/ExecuteLooseScriptCommand.cs new file mode 100644 index 00000000..b6952370 --- /dev/null +++ b/src/ScriptCs/Command/ExecuteLooseScriptCommand.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Linq; +using ScriptCs.Contracts; + +namespace ScriptCs.Command +{ + internal class ExecuteLooseScriptCommand : ExecuteScriptCommandBase, IExecuteLooseScriptCommand + { + public ExecuteLooseScriptCommand( + string script, string[] scriptArgs, + IFileSystem fileSystem, IScriptExecutor scriptExecutor, + IScriptPackResolver scriptPackResolver, + ILogProvider logProvider, + IAssemblyResolver assemblyResolver, + IScriptLibraryComposer composer) : + base(script, scriptArgs, fileSystem, scriptExecutor, scriptPackResolver, logProvider, assemblyResolver, composer) + { + } + + public override CommandResult Execute() + { + try + { + var assemblyPaths = Enumerable.Empty(); + var workingDirectory = FileSystem.CurrentDirectory; + assemblyPaths = AssemblyResolver.GetAssemblyPaths(workingDirectory); + Composer.Compose(workingDirectory); + + ScriptExecutor.Initialize(assemblyPaths, _scriptPackResolver.GetPacks()); + + // HACK: This is a (dirty) fix for #1086. This might be a temporary solution until some further refactoring can be done. + ScriptExecutor.ScriptEngine.CacheDirectory = Path.Combine(workingDirectory ?? FileSystem.CurrentDirectory, FileSystem.DllCacheFolder); + var scriptResult = ScriptExecutor.ExecuteScript(Script, ScriptArgs); + var commandResult = Inspect(scriptResult); + ScriptExecutor.Terminate(); + return commandResult; + } + catch (Exception ex) + { + Logger.ErrorException("Error executing script '{0}'", ex, Script); + return CommandResult.Error; + } + } + } +} diff --git a/src/ScriptCs/Command/ExecuteReplCommand.cs b/src/ScriptCs/Command/ExecuteReplCommand.cs index 98817dd5..fb2a2287 100644 --- a/src/ScriptCs/Command/ExecuteReplCommand.cs +++ b/src/ScriptCs/Command/ExecuteReplCommand.cs @@ -1,6 +1,5 @@ using System; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Command { @@ -14,7 +13,6 @@ internal class ExecuteReplCommand : IExecuteReplCommand private readonly ILog _logger; private readonly IConsole _console; private readonly IAssemblyResolver _assemblyResolver; - private readonly IFileSystemMigrator _fileSystemMigrator; private readonly IScriptLibraryComposer _composer; public ExecuteReplCommand( @@ -23,19 +21,17 @@ public ExecuteReplCommand( IFileSystem fileSystem, IScriptPackResolver scriptPackResolver, IRepl repl, - ILog logger, + ILogProvider logProvider, IConsole console, IAssemblyResolver assemblyResolver, - IFileSystemMigrator fileSystemMigrator, IScriptLibraryComposer composer) { Guard.AgainstNullArgument("fileSystem", fileSystem); Guard.AgainstNullArgument("scriptPackResolver", scriptPackResolver); Guard.AgainstNullArgument("repl", repl); - Guard.AgainstNullArgument("logger", logger); + Guard.AgainstNullArgument("logProvider", logProvider); Guard.AgainstNullArgument("console", console); Guard.AgainstNullArgument("assemblyResolver", assemblyResolver); - Guard.AgainstNullArgument("fileSystemMigrator", fileSystemMigrator); Guard.AgainstNullArgument("composer", composer); _scriptName = scriptName; @@ -43,22 +39,16 @@ public ExecuteReplCommand( _fileSystem = fileSystem; _scriptPackResolver = scriptPackResolver; _repl = repl; - _logger = logger; + _logger = logProvider.ForCurrentType(); _console = console; _assemblyResolver = assemblyResolver; - _fileSystemMigrator = fileSystemMigrator; _composer = composer; } - public string[] ScriptArgs - { - get { return _scriptArgs; } - } + public string[] ScriptArgs => _scriptArgs; public CommandResult Execute() { - _fileSystemMigrator.Migrate(); - _console.WriteLine("scriptcs (ctrl-c to exit or :help for help)" + Environment.NewLine); var workingDirectory = _fileSystem.CurrentDirectory; @@ -103,11 +93,14 @@ public CommandResult Execute() private bool ExecuteLine(IRepl repl) { - _console.Write(string.IsNullOrWhiteSpace(repl.Buffer) ? "> " : "* "); - + var prompt = string.IsNullOrWhiteSpace (repl.Buffer) ? "> " : "* "; + try { - var line = _console.ReadLine(); + var line = _console.ReadLine(prompt); + + if (line == null) + return false; if (!string.IsNullOrWhiteSpace(line)) { diff --git a/src/ScriptCs/Command/ExecuteScriptCommand.cs b/src/ScriptCs/Command/ExecuteScriptCommand.cs index 082874f7..f55100f9 100644 --- a/src/ScriptCs/Command/ExecuteScriptCommand.cs +++ b/src/ScriptCs/Command/ExecuteScriptCommand.cs @@ -2,115 +2,50 @@ using System.IO; using System.Linq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Command { - internal class ExecuteScriptCommand : IScriptCommand + internal class ExecuteScriptCommand : ExecuteScriptCommandBase, IScriptCommand { - private readonly string _script; - private readonly IFileSystem _fileSystem; - private readonly IScriptExecutor _scriptExecutor; - private readonly IScriptPackResolver _scriptPackResolver; - private readonly ILog _logger; - private readonly IAssemblyResolver _assemblyResolver; - private readonly IFileSystemMigrator _fileSystemMigrator; - private readonly IScriptLibraryComposer _composer; - public ExecuteScriptCommand( - string script, - string[] scriptArgs, - IFileSystem fileSystem, - IScriptExecutor scriptExecutor, - IScriptPackResolver scriptPackResolver, - ILog logger, - IAssemblyResolver assemblyResolver, - IFileSystemMigrator fileSystemMigrator, - IScriptLibraryComposer composer - ) + string script, string[] scriptArgs, + IFileSystem fileSystem, IScriptExecutor scriptExecutor, + IScriptPackResolver scriptPackResolver, + ILogProvider logProvider, + IAssemblyResolver assemblyResolver, + IScriptLibraryComposer composer) : + base(script, scriptArgs, fileSystem, scriptExecutor, scriptPackResolver, logProvider, assemblyResolver, composer) { - Guard.AgainstNullArgument("fileSystem", fileSystem); - Guard.AgainstNullArgument("scriptExecutor", scriptExecutor); - Guard.AgainstNullArgument("scriptPackResolver", scriptPackResolver); - Guard.AgainstNullArgument("logger", logger); - Guard.AgainstNullArgument("assemblyResolver", assemblyResolver); - Guard.AgainstNullArgument("fileSystemMigrator", fileSystemMigrator); - Guard.AgainstNullArgument("composer", composer); - - _script = script; - ScriptArgs = scriptArgs; - _fileSystem = fileSystem; - _scriptExecutor = scriptExecutor; - _scriptPackResolver = scriptPackResolver; - _logger = logger; - _assemblyResolver = assemblyResolver; - _fileSystemMigrator = fileSystemMigrator; - _composer = composer; } - public string[] ScriptArgs { get; private set; } - - public CommandResult Execute() + public override CommandResult Execute() { try { - _fileSystemMigrator.Migrate(); - var assemblyPaths = Enumerable.Empty(); - var workingDirectory = _fileSystem.GetWorkingDirectory(_script); + var workingDirectory = FileSystem.GetWorkingDirectory(Script); if (workingDirectory != null) { - assemblyPaths = _assemblyResolver.GetAssemblyPaths(workingDirectory); + assemblyPaths = AssemblyResolver.GetAssemblyPaths(workingDirectory); } - _composer.Compose(workingDirectory); + Composer.Compose(workingDirectory); + + ScriptExecutor.Initialize(assemblyPaths, _scriptPackResolver.GetPacks(), ScriptArgs); + + // HACK: This is a (dirty) fix for #1086. This might be a temporary solution until some further refactoring can be done. + ScriptExecutor.ScriptEngine.CacheDirectory = Path.Combine(workingDirectory ?? FileSystem.CurrentDirectory, FileSystem.DllCacheFolder); + var scriptResult = ScriptExecutor.Execute(Script, ScriptArgs); - _scriptExecutor.Initialize(assemblyPaths, _scriptPackResolver.GetPacks(), ScriptArgs); - var scriptResult = _scriptExecutor.Execute(_script, ScriptArgs); var commandResult = Inspect(scriptResult); - _scriptExecutor.Terminate(); + ScriptExecutor.Terminate(); return commandResult; } - catch (FileNotFoundException ex) - { - _logger.ErrorFormat("{0} - '{1}'.", ex, ex.Message, ex.FileName); - return CommandResult.Error; - } catch (Exception ex) { - _logger.ErrorException("Error executing script '{0}'", ex, _script); - return CommandResult.Error; - } - } - - private CommandResult Inspect(ScriptResult result) - { - if (result == null) - { - return CommandResult.Error; - } - - if (result.CompileExceptionInfo != null) - { - var ex = result.CompileExceptionInfo.SourceException; - _logger.ErrorException("Script compilation failed.", ex); - return CommandResult.Error; - } - - if (result.ExecuteExceptionInfo != null) - { - var ex = result.ExecuteExceptionInfo.SourceException; - _logger.ErrorException("Script execution failed.", ex); + Logger.ErrorException("Error executing script '{0}'", ex, Script); return CommandResult.Error; } - - if (!result.IsCompleteSubmission) - { - _logger.Error("The script is incomplete."); - return CommandResult.Error; - } - - return CommandResult.Success; } } } diff --git a/src/ScriptCs/Command/ICommand.cs b/src/ScriptCs/Command/ICommand.cs index 29ea7cf5..705eb21f 100644 --- a/src/ScriptCs/Command/ICommand.cs +++ b/src/ScriptCs/Command/ICommand.cs @@ -11,6 +11,10 @@ public interface IExecuteReplCommand : IScriptCommand { } + public interface IExecuteLooseScriptCommand : ICommand + { + } + public interface ISaveCommand : ICommand { } diff --git a/src/ScriptCs/Command/InstallCommand.cs b/src/ScriptCs/Command/InstallCommand.cs index 25889860..d1e960dc 100644 --- a/src/ScriptCs/Command/InstallCommand.cs +++ b/src/ScriptCs/Command/InstallCommand.cs @@ -3,7 +3,7 @@ using System.IO; using System.Runtime.Versioning; using ScriptCs.Contracts; -using ScriptCs.Logging; +using ScriptCs.Hosting.Package; namespace ScriptCs.Command { @@ -26,7 +26,7 @@ public InstallCommand( IPackageAssemblyResolver packageAssemblyResolver, IPackageInstaller packageInstaller, IScriptLibraryComposer composer, - ILog logger) + ILogProvider logger) { _name = name; _version = version ?? string.Empty; @@ -35,7 +35,7 @@ public InstallCommand( _packageAssemblyResolver = packageAssemblyResolver; _packageInstaller = packageInstaller; _composer = composer; - _logger = logger; + _logger = logger.ForCurrentType(); } public CommandResult Execute() @@ -63,7 +63,11 @@ public CommandResult Execute() } catch (Exception ex) { - _logger.ErrorFormat("Package installation failed: {0}.", ex, ex.Message); + if (ex is AggregateException agrEx) + { + ex = agrEx.Flatten().InnerException; + } + _logger.ErrorException("Package installation failed.", ex); return CommandResult.Error; } } @@ -81,7 +85,7 @@ private IEnumerable GetPackages(string workingDirectory) yield break; } - yield return new PackageReference(_name, new FrameworkName(".NETFramework,Version=v4.0"), _version); + yield return new PackageReference(_name, new FrameworkName(FrameworkUtils.FrameworkName), _version); } } } \ No newline at end of file diff --git a/src/ScriptCs/Command/SaveCommand.cs b/src/ScriptCs/Command/SaveCommand.cs index 9762a27c..70819d6a 100644 --- a/src/ScriptCs/Command/SaveCommand.cs +++ b/src/ScriptCs/Command/SaveCommand.cs @@ -1,6 +1,5 @@ using System; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Command { @@ -11,14 +10,15 @@ internal class SaveCommand : ISaveCommand private readonly IFileSystem _fileSystem; private readonly ILog _logger; - public SaveCommand(IPackageAssemblyResolver packageAssemblyResolver, IFileSystem fileSystem, ILog logger) + public SaveCommand(IPackageAssemblyResolver packageAssemblyResolver, IFileSystem fileSystem, ILogProvider logProvider) { Guard.AgainstNullArgument("packageAssemblyResolver", packageAssemblyResolver); Guard.AgainstNullArgument("fileSystem", fileSystem); + Guard.AgainstNullArgument("logProvider", logProvider); _packageAssemblyResolver = packageAssemblyResolver; _fileSystem = fileSystem; - _logger = logger; + _logger = logProvider.ForCurrentType(); } public CommandResult Execute() diff --git a/src/ScriptCs/Command/WatchScriptCommand.cs b/src/ScriptCs/Command/WatchScriptCommand.cs index 2ab06183..3c1a0f44 100644 --- a/src/ScriptCs/Command/WatchScriptCommand.cs +++ b/src/ScriptCs/Command/WatchScriptCommand.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Command { @@ -17,7 +16,6 @@ internal class WatchScriptCommand : IScriptCommand private readonly IConsole _console; private readonly IFileSystem _fileSystem; private readonly ILog _logger; - private readonly IFileSystemMigrator _fileSystemMigrator; private readonly CrossAppDomainExecuteScriptCommand _executeScriptCommand; public WatchScriptCommand( @@ -25,22 +23,19 @@ public WatchScriptCommand( string[] scriptArgs, IConsole console, IFileSystem fileSystem, - ILog logger, - IFileSystemMigrator fileSystemMigrator) + ILogProvider logProvider) { Guard.AgainstNullArgument("config", config); Guard.AgainstNullArgument("scriptArgs", scriptArgs); Guard.AgainstNullArgument("console", console); Guard.AgainstNullArgument("fileSystem", fileSystem); - Guard.AgainstNullArgument("logger", logger); - Guard.AgainstNullArgument("fileSystemMigrator", fileSystemMigrator); + Guard.AgainstNullArgument("logProvider", logProvider); _config = config; _scriptArgs = scriptArgs; _console = console; _fileSystem = fileSystem; - _logger = logger; - _fileSystemMigrator = fileSystemMigrator; + _logger = logProvider.ForCurrentType(); _executeScriptCommand = new CrossAppDomainExecuteScriptCommand { @@ -51,8 +46,6 @@ public WatchScriptCommand( public CommandResult Execute() { - _fileSystemMigrator.Migrate(); - _console.WriteLine("scriptcs (ctrl-c to exit)"); _logger.InfoFormat("Running script '{0}' and watching for changes...", _config.ScriptName); @@ -108,9 +101,6 @@ private void EnsureUnloaded(AppDomain domain) } } - public string[] ScriptArgs - { - get { return this._scriptArgs; } - } + public string[] ScriptArgs => this._scriptArgs; } } diff --git a/src/ScriptCs/Config.cs b/src/ScriptCs/Config.cs index fa4993bb..f3cc226e 100644 --- a/src/ScriptCs/Config.cs +++ b/src/ScriptCs/Config.cs @@ -20,8 +20,8 @@ public Config() public string[] Modules { - get { return _modules ?? new string[0]; } - set { _modules = value; } + get => _modules ?? new string[0]; + set => _modules = value; } public string OutputFile { get; set; } @@ -52,14 +52,16 @@ public string[] Modules public bool Watch { get; set; } - public static Config Create(ScriptCsArgs commandArgs) + public string Eval { get; set; } + + public static Config Create(string maskFile, ConfigMask configMask) { - Guard.AgainstNullArgument("commandArgs", commandArgs); + Guard.AgainstNullArgument("configMask", configMask); return new Config() .Apply(ConfigMask.ReadGlobalOrDefault()) - .Apply(commandArgs.Config == null ? ConfigMask.ReadLocalOrDefault() : ConfigMask.Read(commandArgs.Config)) - .Apply(ConfigMask.Create(commandArgs)); + .Apply(maskFile == null ? ConfigMask.ReadLocalOrDefault() : ConfigMask.Read(maskFile)) + .Apply(configMask); } public Config Apply(ConfigMask mask) @@ -96,6 +98,7 @@ public Config Apply(ConfigMask mask) Debug = mask.Debug ?? Debug, Repl = mask.Repl ?? Repl, ScriptName = scriptName ?? ScriptName, + Eval = mask.Eval ?? Eval, Watch = mask.Watch ?? Watch, }; } diff --git a/src/ScriptCs/ConfigMask.cs b/src/ScriptCs/ConfigMask.cs index ed2ae90b..53120ea4 100644 --- a/src/ScriptCs/ConfigMask.cs +++ b/src/ScriptCs/ConfigMask.cs @@ -2,10 +2,11 @@ using System.Globalization; using System.IO; using Newtonsoft.Json; -using ScriptCs.Contracts; namespace ScriptCs { + using ScriptCs.Contracts; + public class ConfigMask { public bool? AllowPreRelease { get; set; } @@ -34,45 +35,15 @@ public class ConfigMask public string ScriptName { get; set; } + public string Eval { get; set; } + public bool? Watch { get; set; } - public static ConfigMask Create(ScriptCsArgs args) - { - Guard.AgainstNullArgument("args", args); + public static ConfigMask ReadGlobalOrDefault() => Read(new FileSystem().GlobalOptsFile, true); - return new ConfigMask - { - AllowPreRelease = args.AllowPreRelease ? (bool?)true : null, - Cache = args.Cache ? (bool?)true : null, - Clean = args.Clean ? (bool?)true : null, - Debug = args.Debug ? (bool?)true : null, - Global = args.Global ? (bool?)true : null, - Install = args.Install, - LogLevel = args.LogLevel, - Modules = args.Modules, - Output = args.Output, - PackageVersion = args.PackageVersion, - Repl = args.Repl ? (bool?)true : null, - Save = args.Save ? (bool?)true : null, - ScriptName = args.ScriptName, - Watch = args.Watch ? (bool?)true : null, - }; - } + public static ConfigMask ReadLocalOrDefault() => Read(Constants.ConfigFilename, true); - public static ConfigMask ReadGlobalOrDefault() - { - return Read(new FileSystem().GlobalOptsFile, true); - } - - public static ConfigMask ReadLocalOrDefault() - { - return Read(Constants.ConfigFilename, true); - } - - public static ConfigMask Read(string path) - { - return Read(path, false); - } + public static ConfigMask Read(string path) => Read(path, false); private static ConfigMask Read(string path, bool defaultIfNotExists) { diff --git a/src/ScriptCs/ExecuteScriptCommandBase.cs b/src/ScriptCs/ExecuteScriptCommandBase.cs new file mode 100644 index 00000000..2c98b677 --- /dev/null +++ b/src/ScriptCs/ExecuteScriptCommandBase.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ScriptCs.Command; +using ScriptCs.Contracts; + +namespace ScriptCs +{ + public abstract class ExecuteScriptCommandBase + { + protected string Script { get; private set; } + protected IFileSystem FileSystem { get; private set; } + protected IScriptExecutor ScriptExecutor { get; private set; } + protected IScriptPackResolver _scriptPackResolver { get; private set; } + protected ILog Logger { get; private set; } + protected IAssemblyResolver AssemblyResolver { get; private set; } + protected IScriptLibraryComposer Composer { get; private set; } + + public ExecuteScriptCommandBase( + string script, + string[] scriptArgs, + IFileSystem fileSystem, + IScriptExecutor scriptExecutor, + IScriptPackResolver scriptPackResolver, + ILogProvider logProvider, + IAssemblyResolver assemblyResolver, + IScriptLibraryComposer composer + ) + { + Guard.AgainstNullArgument("fileSystem", fileSystem); + Guard.AgainstNullArgument("scriptExecutor", scriptExecutor); + Guard.AgainstNullArgument("scriptPackResolver", scriptPackResolver); + Guard.AgainstNullArgument("logProvider", logProvider); + Guard.AgainstNullArgument("assemblyResolver", assemblyResolver); + Guard.AgainstNullArgument("composer", composer); + + Script = script; + ScriptArgs = scriptArgs; + FileSystem = fileSystem; + ScriptExecutor = scriptExecutor; + _scriptPackResolver = scriptPackResolver; + Logger = logProvider.ForCurrentType(); + AssemblyResolver = assemblyResolver; + Composer = composer; + } + + public string[] ScriptArgs { get; private set; } + + public abstract CommandResult Execute(); + + protected CommandResult Inspect(ScriptResult result) + { + if (result == null) + { + return CommandResult.Error; + } + + if (result.CompileExceptionInfo != null) + { + var ex = result.CompileExceptionInfo.SourceException; + Logger.ErrorException("Script compilation failed.", ex); + return CommandResult.Error; + } + + if (result.ExecuteExceptionInfo != null) + { + var ex = result.ExecuteExceptionInfo.SourceException; + Logger.ErrorException("Script execution failed.", ex); + return CommandResult.Error; + } + + if (!result.IsCompleteSubmission) + { + Logger.Error("The script is incomplete."); + return CommandResult.Error; + } + + return CommandResult.Success; + } + } +} diff --git a/src/ScriptCs/Program.cs b/src/ScriptCs/Program.cs index daca7795..789353ee 100644 --- a/src/ScriptCs/Program.cs +++ b/src/ScriptCs/Program.cs @@ -1,4 +1,7 @@ -using System; +using McMaster.Extensions.CommandLineUtils; +using ScriptCs.Contracts; +using System; +using System.Diagnostics; using System.IO; using System.Linq; @@ -9,43 +12,103 @@ internal static class Program [LoaderOptimizationAttribute(LoaderOptimization.MultiDomain)] private static int Main(string[] args) { + //args = args.Skip(2).ToArray(); ProfileOptimizationShim.SetProfileRoot(Path.GetDirectoryName(typeof(Program).Assembly.Location)); ProfileOptimizationShim.StartProfile(typeof(Program).Assembly.GetName().Name + ".profile"); var nonScriptArgs = args.TakeWhile(arg => arg != "--").ToArray(); var scriptArgs = args.Skip(nonScriptArgs.Length + 1).ToArray(); - ScriptCsArgs commandArgs; - try + var app = new CommandLineApplication(throwOnUnexpectedArg: true) { - commandArgs = ScriptCsArgs.Parse(nonScriptArgs); - } - catch (Exception ex) + ExtendedHelpText = "Usage: scriptcs options", + OptionsComparison = StringComparison.OrdinalIgnoreCase + }; + + var script = app.Argument("script", "Script file name, must be specified first"); + + var scriptNameFallback = app.Option("--scriptname | -script", "Alternative way to pass a script filename", CommandOptionType.NoValue); + var repl = app.Option("--repl | -r", "Launch REPL mode when running script. To just launch REPL, simply omit the 'script' argument", CommandOptionType.NoValue); + var eval = app.Option("--eval | -e", "Code to immediately evaluate", CommandOptionType.SingleValue); + var configFile = app.Option("--config | -co", "Defines config file name", CommandOptionType.SingleValue); + var debug = app.Option("--debug | -d", "Emits PDB symbols allowing for attaching a Visual Studio debugger", CommandOptionType.NoValue); + var version = app.Option("--version | -v", "Outputs version information", CommandOptionType.NoValue); + var cache = app.Option("--cache | -c", "Flag which determines whether to run in memory or from a .dll", CommandOptionType.NoValue); + var logLevel = app.Option("--loglevel | -log", "Flag which defines the log level used", CommandOptionType.SingleValue); + var watch = app.Option("--watch | -w", "Watch the script file and reload it when changed", CommandOptionType.NoValue); + var modules = app.Option("--modules | -m", "Specify modules to load (comma separated)", CommandOptionType.SingleValue); + var output = app.Option("--output | -o", "Write all console output to the specified file", CommandOptionType.SingleValue); + + app.HelpOption("-Help | -?"); + + app.Command("install", c => { - Console.WriteLine(ex.Message); - Console.WriteLine(ScriptCsArgs.GetUsage()); - return 1; - } + var package = c.Argument("package", "Specific package to install, otherwise installs and restores packages which are specified in packages.config"); + var allowPrerelease = c.Option("--allowprerelease | -pre", "Allows installation of packages' prelease versions", CommandOptionType.NoValue); + var packageVersion = c.Option("--packageversion | -p", "Defines the version of the package to install", CommandOptionType.SingleValue); + var save = c.Option("--save | -s", "Creates a packages.config file based on the packages directory", CommandOptionType.NoValue); + var clean = c.Option("--clean | -cl", "Cleans installed packages from working directory", CommandOptionType.NoValue); + var global = c.Option("--global | -g", "Installs and restores global packages which are specified in packages.config", CommandOptionType.NoValue); + + c.OnExecute(() => + { + var configMask = new ConfigMask + { + Repl = false, + Global = global.HasValue() ? true : (bool?)null, + Install = package.Value ?? string.Empty, // needed to trigger install of all packages! + PackageVersion = packageVersion.Value(), + AllowPreRelease = allowPrerelease.HasValue() ? true : (bool?)null, + Save = save.HasValue() ? true : (bool?)null, + Clean = clean.HasValue() ? true : (bool?)null, + Output = output.Value(), + Debug = debug.HasValue() ? true : (bool?)null, + LogLevel = logLevel.ParsedValue + }; - if (commandArgs.Help) + return Application.Run(Config.Create(configFile.Value(), configMask), scriptArgs); + }); + }); + + app.OnExecute(() => { - Console.WriteLine(ScriptCsArgs.GetUsage()); - return 0; - } + if (configFile.HasValue() && !File.Exists(configFile.Value())) + { + Console.WriteLine("The specified config file does not exist."); + return 1; + } + + if (version.HasValue()) + { + VersionWriter.Write(); + return 0; + } + + var configMask = new ConfigMask + { + Repl = repl.HasValue() ? true : (bool?)null, + ScriptName = scriptNameFallback.HasValue() ? scriptNameFallback.Value() : script.Value, + Debug = debug.HasValue() ? true : (bool?)null, + Eval = eval.Value(), + Cache = cache.HasValue() ? true : (bool?)null, + Watch = watch.HasValue() ? true : (bool?)null, + LogLevel = logLevel.ParsedValue, + Modules = modules.Value(), + Output = output.Value() + }; - if (commandArgs.Version) + return Application.Run(Config.Create(configFile.Value(), configMask), scriptArgs); + }); + + try { - VersionWriter.Write(); - return 0; + return app.Execute(nonScriptArgs); } - - if (commandArgs.Config != null && !File.Exists(commandArgs.Config)) + catch (CommandParsingException) { - Console.WriteLine("The specified config file does not exist."); + app.ShowHelp(); return 1; } - - return Application.Run(Config.Create(commandArgs), scriptArgs); } } } diff --git a/src/ScriptCs/Properties/AssemblyInfo.cs b/src/ScriptCs/Properties/AssemblyInfo.cs index fb74533d..707f917b 100644 --- a/src/ScriptCs/Properties/AssemblyInfo.cs +++ b/src/ScriptCs/Properties/AssemblyInfo.cs @@ -3,11 +3,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("scriptcs")] -[assembly: AssemblyDescription("scriptcs command line tool.")] - [assembly: Guid("f624ee58-910a-4541-a46f-afcd3edca9df")] - [assembly: NeutralResourcesLanguage("en-US")] - [assembly: InternalsVisibleTo("ScriptCs.Tests")] diff --git a/src/ScriptCs/Properties/Settings.Designer.cs b/src/ScriptCs/Properties/Settings.Designer.cs index 0f094d02..3d2dfa89 100644 --- a/src/ScriptCs/Properties/Settings.Designer.cs +++ b/src/ScriptCs/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18051 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace ScriptCs.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.5.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/src/ScriptCs/Properties/chocolateyInstall.ps1 b/src/ScriptCs/Properties/chocolateyInstall.ps1 index 3e48a5ad..85c2846b 100644 --- a/src/ScriptCs/Properties/chocolateyInstall.ps1 +++ b/src/ScriptCs/Properties/chocolateyInstall.ps1 @@ -1,18 +1,5 @@ try { $tools = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" - $nuget = "$env:ChocolateyInstall\ChocolateyInstall\nuget" - $nugetPath = "$tools\nugets" - - Write-Host "Retrieving NuGet dependencies..." -ForegroundColor DarkYellow - - $dependencies = @{ - "Roslyn.Compilers.CSharp" = "1.2.20906.2"; - } - - $dependencies.GetEnumerator() | %{ &nuget install $_.Name -version $_.Value -o $nugetPath } - - Get-ChildItem $nugetPath -Filter "*.dll" -Recurse | %{ Copy-Item $_.FullName $tools -Force } - Remove-Item $nugetPath -Recurse -Force if (Test-Path "$tools\..\lib") { Remove-Item "$tools\..\lib" -Recurse -Force diff --git a/src/ScriptCs/Properties/scriptcs.nuspec b/src/ScriptCs/Properties/scriptcs.nuspec index a1747bd1..610aa737 100644 --- a/src/ScriptCs/Properties/scriptcs.nuspec +++ b/src/ScriptCs/Properties/scriptcs.nuspec @@ -13,15 +13,12 @@ - - - - - - - - + + + + + \ No newline at end of file diff --git a/src/ScriptCs/ScriptCs.csproj b/src/ScriptCs/ScriptCs.csproj index bee114c8..20e9f2dc 100644 --- a/src/ScriptCs/ScriptCs.csproj +++ b/src/ScriptCs/ScriptCs.csproj @@ -1,176 +1,29 @@ - - - + - Debug - AnyCPU - {25080671-1A80-4041-B9C7-260578FF4849} + 1.0.0 Exe - ScriptCs + net461 + scriptcs + Glenn Block, Filip Wojcieszyn, Justin Rusbatch + https://github.com/scriptcs/scriptcs/blob/master/LICENSE.md + http://scriptcs.net + http://www.gravatar.com/avatar/5c754f646971d8bc800b9d4057931938.png?s=120 + scriptcs + Write .Net apps with a text editor, NuGet, and the power of Roslyn! + roslyn csx script scriptcs + scriptcs command line tool. scriptcs - Properties - 512 - v4.5 - - ..\..\ - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - true - full - false - bin\debug\ - DEBUG;TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ..\..\ScriptCs.ruleset - - - ..\..\common\Icon.ico - - - - ..\..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll - - - False - ..\..\packages\PowerArgs.2.0.5\lib\net40\PowerArgs.dll - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - Guard.cs - - - - - - - - - - - - - - - - - - - - - - True - True - Settings.settings - - - - - - - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - Always - - - - Properties\Icon.ico - + - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - - - {e4adcfee-ff3b-4ef5-8298-2b31f407f58b} - ScriptCs.Engine.Mono - - - {e79ec231-e27d-4057-91c9-2d001a3a8c3b} - ScriptCs.Engine.Roslyn - - - {9aef2d95-87fb-4829-b384-34bfe076d531} - ScriptCs.Hosting - + + + + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/ScriptCs/ScriptCsArgs.cs b/src/ScriptCs/ScriptCsArgs.cs deleted file mode 100644 index f4fb8cab..00000000 --- a/src/ScriptCs/ScriptCsArgs.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using PowerArgs; -using ScriptCs.Contracts; - -namespace ScriptCs -{ - [ArgExample( - "scriptcs server.csx -logLevel debug", - "Executes the 'server.csx' script and displays detailed log messages. Useful for debugging.")] - public class ScriptCsArgs - { - [ArgDescription("Launch REPL mode when running script. To just launch REPL, simply omit the 'script' argument.")] - public bool Repl { get; set; } - - [ArgPosition(0)] - [ArgShortcut("script")] - [ArgDescription("Script file name, must be specified first")] - public string ScriptName { get; set; } - - [ArgShortcut("?")] - [ArgDescription("Displays help")] - public bool Help { get; set; } - - [ArgDescription("Emits PDB symbols allowing for attaching a Visual Studio debugger")] - public bool Debug { get; set; } - - [ArgDescription("Flag which determines whether to run in memory or from a .dll")] - public bool Cache { get; set; } - - [ArgShortcut("log")] - [ArgDescription("Flag which defines the log level used.")] - public LogLevel? LogLevel { get; set; } - - [ArgDescription("Installs and restores packages which are specified in packages.config")] - [ArgShortcut("i")] - public string Install { get; set; } - - [ArgShortcut("g")] - [ArgDescription("Installs and restores global packages which are specified in packages.config")] - public bool Global { get; set; } - - [ArgDescription("Creates a packages.config file based on the packages directory")] - public bool Save { get; set; } - - [ArgDescription("Cleans installed packages from working directory")] - public bool Clean { get; set; } - - [ArgShortcut("pre")] - [ArgDescription("Allows installation of packages' prelease versions")] - public bool AllowPreRelease { get; set; } - - [ArgDescription("Outputs version information")] - public bool Version { get; set; } - - [ArgDescription("Watch the script file and reload it when changed")] - public bool Watch { get; set; } - - [ArgDescription("Specify modules to load")] - public string Modules { get; set; } - - [ArgDescription("Defines config file name")] - public string Config { get; set; } - - [ArgDescription("Defines the version of the package to install. Used in conjunction with -install")] - public string PackageVersion { get; set; } - - [ArgDescription("Write all console output to the specified file")] - public string Output { get; set; } - - public static ScriptCsArgs Parse(string[] args) - { - Guard.AgainstNullArgument("args", args); - - var curatedArgs = new List(); - string implicitPackageVersion = null; - for (var index = 0; index < args.Length; ++index) - { - if (index < args.Length - 2 && - (string.Equals(args[index], "-install", StringComparison.OrdinalIgnoreCase) || - string.Equals(args[index], "-i", StringComparison.OrdinalIgnoreCase)) && - !args[index + 1].StartsWith("-", StringComparison.Ordinal) && - !args[index + 2].StartsWith("-", StringComparison.Ordinal)) - { - curatedArgs.Add(args[index]); - curatedArgs.Add(args[index + 1]); - implicitPackageVersion = args[index + 2]; - index += 2; - } - else - { - curatedArgs.Add(args[index]); - } - } - - var scriptCsArgs = Args.Parse(curatedArgs.ToArray()); - scriptCsArgs.PackageVersion = scriptCsArgs.PackageVersion ?? implicitPackageVersion; - return scriptCsArgs; - } - - public static string GetUsage() - { - return ArgUsage.GetUsage( - null, new ArgUsageOptions { ShowPosition = false, ShowType = false, }); - } - } -} diff --git a/src/ScriptCs/ScriptServicesBuilderFactory.cs b/src/ScriptCs/ScriptServicesBuilderFactory.cs index 76e68f38..c5450516 100644 --- a/src/ScriptCs/ScriptServicesBuilderFactory.cs +++ b/src/ScriptCs/ScriptServicesBuilderFactory.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; +using System.IO; using ScriptCs.Contracts; using ScriptCs.Hosting; -using ScriptCs.Logging; -using LogLevel = ScriptCs.Contracts.LogLevel; namespace ScriptCs { @@ -20,12 +17,17 @@ public static IScriptServicesBuilder Create(Config config, string[] scriptArgs) console = new FileConsole(config.OutputFile, console); } - var configurator = new LoggerConfigurator(config.LogLevel); - configurator.Configure(console, new NoOpLogger()); - var logger = configurator.GetLogger(); - var initializationServices = new InitializationServices(logger); + var logProvider = new ColoredConsoleLogProvider(config.LogLevel, console); + var initializationServices = new InitializationServices(logProvider); initializationServices.GetAppDomainAssemblyResolver().Initialize(); + if (config.ScriptName != null && Path.GetFileName(config.ScriptName) != config.ScriptName) + { + var path = Path.GetFullPath(config.ScriptName); + initializationServices.GetFileSystem().CurrentDirectory = Path.GetDirectoryName(path); + config.ScriptName = path; + } + // NOTE (adamralph): this is a hideous assumption about what happens inside the CommandFactory. // It is a result of the ScriptServicesBuilderFactory also having to know what is going to happen inside the // Command Factory so that it builds the builder(:-p) correctly in advance. @@ -34,7 +36,7 @@ public static IScriptServicesBuilder Create(Config config, string[] scriptArgs) var repl = config.Repl || (!config.Clean && config.PackageName == null && !config.Save && config.ScriptName == null); - var scriptServicesBuilder = new ScriptServicesBuilder(console, logger, null, null, initializationServices) + var scriptServicesBuilder = new ScriptServicesBuilder(console, logProvider, null, initializationServices) .Cache(config.Cache) .Debug(config.Debug) .LogLevel(config.LogLevel) @@ -43,14 +45,5 @@ public static IScriptServicesBuilder Create(Config config, string[] scriptArgs) return scriptServicesBuilder.LoadModules(Path.GetExtension(config.ScriptName) ?? ".csx", config.Modules); } - - private class NoOpLogger : ILog - { - public bool Log( - Logging.LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) - { - return false; - } - } } } diff --git a/src/ScriptCs/app.config b/src/ScriptCs/app.config index 434e45fb..a61f4a50 100644 --- a/src/ScriptCs/app.config +++ b/src/ScriptCs/app.config @@ -1,6 +1,10 @@  - + - \ No newline at end of file + + + + + diff --git a/src/ScriptCs/packages.config b/src/ScriptCs/packages.config deleted file mode 100644 index 7f334276..00000000 --- a/src/ScriptCs/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Core.Tests/AppDomainAssemblyResolverTests.cs b/test/ScriptCs.Core.Tests/AppDomainAssemblyResolverTests.cs index a55acaf5..a20b191d 100644 --- a/test/ScriptCs.Core.Tests/AppDomainAssemblyResolverTests.cs +++ b/test/ScriptCs.Core.Tests/AppDomainAssemblyResolverTests.cs @@ -4,12 +4,12 @@ using System.Linq; using System.Reflection; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; -using ScriptCs.Logging; using Should; using Xunit; using Xunit.Extensions; +using AutoFixture.Xunit2; +using System.Threading; namespace ScriptCs.Tests { @@ -26,27 +26,28 @@ static AppDomainAssemblyResolverTests() public class TheConstructor { - [Theory, ScriptCsAutoData] + [Theory(Skip = "AppDomain events are flaky here - needs to be reviewed"), ScriptCsAutoData] public void ShouldSubscribeToTheResolveEvent( - ILog logger, + TestLogProvider logProvider, IFileSystem fileSystem, IAssemblyResolver assemblyResolver, [Frozen] Mock assemblyUtilityMock) { - bool called = false; + var autoResetEvent = new AutoResetEvent(false); assemblyUtilityMock.Setup(a => a.IsManagedAssembly(It.IsAny())).Returns(true); var assemblyUtility = assemblyUtilityMock.Object; - new AppDomainAssemblyResolver(logger, fileSystem, assemblyResolver, + new AppDomainAssemblyResolver(logProvider, fileSystem, assemblyResolver, assemblyUtility, resolveHandler: (o, r) => { - called = true; + autoResetEvent.Set(); return Assembly.GetExecutingAssembly(); } ); Assembly.Load("test"); + var called = autoResetEvent.WaitOne(1000); called.ShouldBeTrue(); } } @@ -114,7 +115,7 @@ public void ShouldRetrieveTheMappedAssemblyInfo() assemblyInfoMapMock.Setup(m => m.TryGetValue(It.IsAny(), out foundInfo)).Returns(false); var resolver = new AppDomainAssemblyResolver( - Mock.Of(), + new TestLogProvider(), Mock.Of(), Mock.Of(), assemblyUtilityMock.Object, @@ -155,13 +156,13 @@ public void ShouldOverrideIfTheAssemblyVersionIsGreaterThanTheMappedAssemblyAndI [Theory, ScriptCsAutoData] public void ShouldLogWhenTheAssemblyIsMapped( [Frozen] Mock assemblyUtilityMock, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, AppDomainAssemblyResolver resolver) { assemblyUtilityMock.Setup(u => u.IsManagedAssembly(It.IsAny())).Returns(true); assemblyUtilityMock.Setup(u => u.GetAssemblyName(_info.Path)).Returns(_assemblyName); resolver.AddAssemblyPaths(new[] { _info.Path }); - ((TestLogger)log).Output.ShouldContain( + logProvider.Output.ShouldContain( "DEBUG: Mapping Assembly " + _assemblyName.Name + " to version:" + _assemblyName.Version); } @@ -169,7 +170,7 @@ public void ShouldLogWhenTheAssemblyIsMapped( public void ShouldWarnIfTheAssemblyVersionIsGreaterThanTheMappedAssemblyAndItWasLoaded( [Frozen] Mock assemblyUtilityMock, [Frozen] IDictionary assemblyInfoMap, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, AppDomainAssemblyResolver resolver) { _info.Version = new Version(0, 0); @@ -178,7 +179,7 @@ public void ShouldWarnIfTheAssemblyVersionIsGreaterThanTheMappedAssemblyAndItWas _info.Assembly = typeof(Mock).Assembly; assemblyInfoMap[_assemblyName.Name] = _info; resolver.AddAssemblyPaths(new[] { _info.Path }); - ((TestLogger)log).Output.ShouldContain( + logProvider.Output.ShouldContain( "WARN: Conflict: Assembly " + _info.Path + " with version " + _assemblyName.Version + " cannot be added as it has already been resolved"); } @@ -225,7 +226,7 @@ public void ShouldRetrieveTheMappedAssemblyInfo() assemblyInfoMapMock.Setup(m => m.TryGetValue(It.IsAny(), out foundInfo)).Returns(false); var resolver = new AppDomainAssemblyResolver( - Mock.Of(), + new TestLogProvider(), Mock.Of(), Mock.Of(), assemblyUtilityMock.Object, @@ -253,7 +254,7 @@ public void ShouldLoadTheAssemblyIfTheMappedAssemblyInfoExistsAndItHasNotBeenLoa [Theory, ScriptCsAutoData] public void ShouldLogTheAssemblyThatIsBeingResolved( [Frozen] Mock assemblyUtilityMock, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, [Frozen] IDictionary assemblyInfoMap, AppDomainAssemblyResolver resolver) { @@ -262,7 +263,7 @@ public void ShouldLogTheAssemblyThatIsBeingResolved( assemblyInfoMap[_assemblyName.Name] = _info; resolver.AssemblyResolve(this, new ResolveEventArgs(_assemblyName.Name)); - ((TestLogger)log).Output.ShouldContain( + logProvider.Output.ShouldContain( "DEBUG: Resolving from: " + _assemblyName.Name + " to: " + _assemblyName.ToString()); } diff --git a/test/ScriptCs.Core.Tests/AssemblyResolverTests.cs b/test/ScriptCs.Core.Tests/AssemblyResolverTests.cs index e54d0b29..29addbe5 100644 --- a/test/ScriptCs.Core.Tests/AssemblyResolverTests.cs +++ b/test/ScriptCs.Core.Tests/AssemblyResolverTests.cs @@ -1,10 +1,10 @@ using System.IO; using System.Linq; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -41,7 +41,6 @@ AssemblyResolver resolver [Theory, ScriptCsAutoData] public void ShouldReturnAssembliesFromBinFolder( [Frozen] Mock fileSystemMock, - [Frozen] Mock packageAssemblyResolverMock, [Frozen] Mock assemblyUtilityMock, AssemblyResolver resolver ) @@ -67,7 +66,6 @@ AssemblyResolver resolver [Theory, ScriptCsAutoData] public void ShouldNotReturnNonManagedAssemblies( [Frozen] Mock fileSystemMock, - [Frozen] Mock packageAssemblyResolverMock, [Frozen] Mock assemblyUtilityMock, AssemblyResolver resolver ) diff --git a/test/ScriptCs.Core.Tests/FileProcessorTests.cs b/test/ScriptCs.Core.Tests/FileProcessorTests.cs index 54d205f3..179a849d 100644 --- a/test/ScriptCs.Core.Tests/FileProcessorTests.cs +++ b/test/ScriptCs.Core.Tests/FileProcessorTests.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; using Moq; using ScriptCs.Contracts; using ScriptCs.Contracts.Exceptions; -using ScriptCs.Logging; using Should; using Xunit; @@ -12,6 +12,51 @@ namespace ScriptCs.Tests { public class FileProcessorTests { + public class TheParseFileMethod + { + private readonly Mock _fileSystem; + + public TheParseFileMethod() + { + _fileSystem = new Mock(); + _fileSystem.SetupGet(x => x.NewLine).Returns(Environment.NewLine); + _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == @"c:\test\main.csx"))) + .Returns(new string[] {"main"}); + _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == @"c:\test\child.csx"))) + .Returns(new string[] {"child"}); + + _fileSystem.Setup(fs => fs.GetFullPath(It.IsAny())).Returns((path) => path); + } + + [Fact] + public void SetsTheScriptPath() + { + var path = @"c:\test\main.csx"; + var processor = GetFilePreProcessor(); + var context = new FileParserContext(); + processor.ParseFile(path, context); + _fileSystem.Verify(x => x.ReadFileLines(It.Is(f => f == path)), Times.Exactly(1)); + context.ScriptPath.ShouldEqual(path); + } + + [Fact] + public void AddsLoadedScripts() + { + var path = @"c:\test\child.csx"; + var processor = GetFilePreProcessor(); + var context = new FileParserContext(); + context.ScriptPath = @"c:\test\main.csx"; + processor.ParseFile(path, context); + _fileSystem.Verify(x => x.ReadFileLines(It.Is(f => f == path)), Times.Exactly(1)); + context.LoadedScripts.ShouldContain(path); + } + + private IFilePreProcessor GetFilePreProcessor() + { + return new FilePreProcessor(_fileSystem.Object, new TestLogProvider(), Enumerable.Empty()); + } + } + public class ProcessFileMethod { private List _file1 = new List @@ -82,7 +127,7 @@ public void MultipleUsingStatementsShouldProduceDistinctOutput() } [Fact] - public void UsingStateMentsShoulAllBeAtTheTop() + public void UsingStateMentsShouldAllBeAtTheTop() { var processor = GetFilePreProcessor(); var result = processor.ProcessFile("script1.csx"); @@ -112,8 +157,7 @@ public void ShouldReturnResultWithAllLoadedFiles() var processor = GetFilePreProcessor(); var result = processor.ProcessFile("script1.csx"); - result.LoadedScripts.Count.ShouldEqual(3); - result.LoadedScripts.ShouldContain("script1.csx"); + result.LoadedScripts.Count.ShouldEqual(2); result.LoadedScripts.ShouldContain("script2.csx"); result.LoadedScripts.ShouldContain("script4.csx"); } @@ -263,7 +307,8 @@ public void ShouldNotThrowStackOverflowExceptionOnLoadLoop() _fileSystem.Setup(x => x.ReadFileLines("A.csx")).Returns(a.ToArray()); _fileSystem.Setup(x => x.ReadFileLines("B.csx")).Returns(b.ToArray()); - Assert.DoesNotThrow(() => GetFilePreProcessor().ProcessFile("A.csx")); + var ex = Record.Exception(() => GetFilePreProcessor().ProcessFile("A.csx")); + Assert.Null(ex); } [Fact] @@ -279,7 +324,7 @@ public void ShouldNotBeAllowedToLoadAfterUsing() _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "file.csx"))).Returns(file.ToArray()); var processor = GetFilePreProcessor(); - Assert.Throws(typeof(InvalidDirectiveUseException), () => processor.ProcessFile("file.csx")); + Assert.Throws(() => processor.ProcessFile("file.csx")); _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "script4.csx")), Times.Never()); } @@ -525,7 +570,7 @@ private IFilePreProcessor GetFilePreProcessor() new ShebangLineProcessor() }; - return new FilePreProcessor(_fileSystem.Object, Mock.Of(), lineProcessors); + return new FilePreProcessor(_fileSystem.Object, new TestLogProvider(), lineProcessors); } } @@ -589,7 +634,7 @@ private IFilePreProcessor GetFilePreProcessor() new ShebangLineProcessor() }; - return new FilePreProcessor(_fileSystem.Object, Mock.Of(), lineProcessors); + return new FilePreProcessor(_fileSystem.Object, new TestLogProvider(), lineProcessors); } } @@ -604,6 +649,22 @@ public TheParseScriptMethod() _fileSystem.Setup(fs => fs.GetFullPath(It.IsAny())).Returns((path) => path); } + [Fact] + public void ShouldAllowSingleLineCommentsBeforeDirectives() + { + var testableDirectiveProcessor = new DirectiveLineProcessorTests.TestableDirectiveLineProcessor(BehaviorAfterCode.Throw); + var filePreprocessor = GetFilePreProcessor(testableDirectiveProcessor, new LoadLineProcessor(_fileSystem.Object)); + var lines = new List + { + "//Test Comment", + "#Test something", + "Console.WriteLine(\"Success\");" + }; + + var ex = Record.Exception(() => filePreprocessor.ParseScript(lines, new FileParserContext())); + Assert.Null(ex); + } + [Fact] public void ShouldProcessCustomDirectiveWhenItComesBeforeCode() { @@ -647,7 +708,7 @@ private IFilePreProcessor GetFilePreProcessor(ILineProcessor customDirectiveProc customDirectiveProcessor }; - return new FilePreProcessor(_fileSystem.Object, Mock.Of(), lineProcessors); + return new FilePreProcessor(_fileSystem.Object, new TestLogProvider(), lineProcessors); } public class TestableLoadLineProcessor : LoadLineProcessor diff --git a/test/ScriptCs.Core.Tests/FileProcessorTests.cs.orig b/test/ScriptCs.Core.Tests/FileProcessorTests.cs.orig deleted file mode 100644 index 93913a57..00000000 --- a/test/ScriptCs.Core.Tests/FileProcessorTests.cs.orig +++ /dev/null @@ -1,476 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using Should; -using Xunit; - -namespace ScriptCs.Tests -{ - public class FileProcessorTests - { -<<<<<<< HEAD - public class TheProcessFileMethod - { - private List file1 = new List - { - "using System;", - @"Console.WriteLine(""Hello Script 1"");", - @"Console.WriteLine(""Loading Script 2"");", - @"#load ""script2.csx""", - @"Console.WriteLine(""Loaded Script 2"");", - @"Console.WriteLine(""Loading Script 4"");", - @"#load ""script4.csx"";", - @"Console.WriteLine(""Loaded Script 4"");", - @"Console.WriteLine(""Goodbye Script 1"");" - }; - - private List file2 = new List - { - "using System;", - @"Console.WriteLine(""Hello Script 2"");", - @"Console.WriteLine(""Loading Script 3"");", - @"#load ""script3.csx""", - @"Console.WriteLine(""Loaded Script 3"");", - @"Console.WriteLine(""Goodbye Script 2"");" - }; - - private List file3 = new List -======= - public class ProcessFileMethod - { - private List _file1 = new List - { - @"#load ""script2.csx""", - @"#load ""script4.csx"";", - "using System;", - @"Console.WriteLine(""Hello Script 1"");", - @"Console.WriteLine(""Loading Script 2"");", - @"Console.WriteLine(""Loaded Script 2"");", - @"Console.WriteLine(""Loading Script 4"");", - @"Console.WriteLine(""Loaded Script 4"");", - @"Console.WriteLine(""Goodbye Script 1"");" - }; - - private List _file2 = new List - { - "using System;", - @"Console.WriteLine(""Hello Script 2"");", - @"Console.WriteLine(""Loading Script 3"");", - @"#load ""script3.csx""", - @"Console.WriteLine(""Loaded Script 3"");", - @"Console.WriteLine(""Goodbye Script 2"");" - }; - - private readonly List _file3 = new List - { - "using System;", - "using System.Collections.Generic;", - @"Console.WriteLine(""Hello Script 3"");", - @"Console.WriteLine(""Goodbye Script 3"");" - }; - - private readonly List _file4 = new List - { - "using System;", - "using System.Core;", - @"Console.WriteLine(""Hello Script 4"");", - @"Console.WriteLine(""Goodbye Script 4"");" - }; - - private readonly Mock _fileSystem; - - public ProcessFileMethod() - { - _fileSystem = new Mock(); - _fileSystem.SetupGet(x => x.NewLine).Returns(Environment.NewLine); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script1.csx"))) - .Returns(_file1.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script2.csx"))) - .Returns(_file2.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script3.csx"))) - .Returns(_file3.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script4.csx"))) - .Returns(_file4.ToArray()); - } - - [Fact] - public void MultipleUsingStatementsShouldProduceDistinctOutput() - { - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i.StartsWith("\\script"))), Times.Exactly(3)); - Assert.Equal(2, splitOutput.Count(x => x.TrimStart(' ').StartsWith("using "))); - } - - [Fact] - public void UsingStateMentsShoulAllBeAtTheTop() ->>>>>>> e46d53e6a34d0ae5aadc5876ae63d9d8af3e278e - { - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - -<<<<<<< HEAD - private List file4 = new List -======= - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - var lastUsing = splitOutput.ToList().FindLastIndex(x => x.TrimStart(' ').StartsWith("using ")); - var firsNotUsing = splitOutput.ToList().FindIndex(x => !x.TrimStart(' ').StartsWith("using ")); - - Assert.True(lastUsing < firsNotUsing); - } - - [Fact] - public void ShouldNotLoadInlineLoads() ->>>>>>> e46d53e6a34d0ae5aadc5876ae63d9d8af3e278e - { - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - -<<<<<<< HEAD - private readonly Mock _fileSystem; - - public TheProcessFileMethod() - { - _fileSystem = new Mock(); - _fileSystem.SetupGet(x => x.NewLine).Returns(Environment.NewLine); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script1.csx"))) - .Returns(file1.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script2.csx"))) - .Returns(file2.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script3.csx"))) - .Returns(file3.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script4.csx"))) - .Returns(file4.ToArray()); - } - - [Fact] - public void MultipleUsingStatementsShouldProduceDistinctOutput() - { - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - - var splitOutput = output.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i.StartsWith("\\script"))), Times.Exactly(4)); - Assert.Equal(3, splitOutput.Count(x => x.TrimStart(' ').StartsWith("using "))); - } - - [Fact] - public void UsingStateMentsShoulAllBeAtTheTop() - { - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - - var splitOutput = output.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - var lastUsing = splitOutput.ToList().FindLastIndex(x => x.TrimStart(' ').StartsWith("using ")); - var firsNotUsing = splitOutput.ToList().FindIndex(x => !x.TrimStart(' ').StartsWith("using ")); - - Assert.True(lastUsing < firsNotUsing); - } - - [Fact] - public void ThreeLoadedFilesShoulAllBeCalledOnce() - { - var processor = new FilePreProcessor(_fileSystem.Object); - processor.ProcessFile("\\script1.csx"); - - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script1.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script2.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script3.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script4.csx")), Times.Once()); - } - - [Fact] - public void LoadBeforeUsingShouldBeAllowed() - { - var file = new List - { - @"#load ""script4.csx""", - "", - "using System;", - @"Console.WriteLine(""abc"");" - }; -======= - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script1.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script2.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script3.csx")), Times.Never()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script4.csx")), Times.Once()); - } - - [Fact] - public void ShouldNotLoadSameFileTwice() - { - var file = new List - { - @"#load ""script4.csx""", - "using System;", - @"Console.WriteLine(""Hello Script 2"");", - }; - - var fs = new Mock(); - fs.Setup(i => i.NewLine).Returns(Environment.NewLine); - fs.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script2.csx"))) - .Returns(file.ToArray()); - fs.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script4.csx"))) - .Returns(_file4.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script1.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script2.csx")), Times.Once()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script3.csx")), Times.Never()); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script4.csx")), Times.Once()); - } - - [Fact] - public void LoadBeforeUsingShouldBeAllowed() - { - var file = new List - { - @"#load ""script4.csx""", - "", - "using System;", - @"Console.WriteLine(""abc"");" - }; - - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\file.csx"))).Returns(file.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\file.csx"); - - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - var lastUsing = splitOutput.ToList().FindLastIndex(x => x.TrimStart(' ').StartsWith("using ")); - var firsNotUsing = splitOutput.ToList().FindIndex(x => !x.TrimStart(' ').StartsWith("using ")); - - splitOutput.Count(x => x.TrimStart(' ').StartsWith("using ")).ShouldEqual(2); - Assert.True(lastUsing < firsNotUsing); - } - - [Fact] - public void ShouldNotBeAllowedToLoadAfterUsing() - { - var file = new List - { - "using System;", - @"Console.WriteLine(""abc"");", - @"#load ""script4.csx""" - }; ->>>>>>> e46d53e6a34d0ae5aadc5876ae63d9d8af3e278e - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\file.csx"))).Returns(file.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\file.csx"); - -<<<<<<< HEAD - var splitOutput = output.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - var lastUsing = splitOutput.ToList().FindLastIndex(x => x.TrimStart(' ').StartsWith("using ")); - var firsNotUsing = splitOutput.ToList().FindIndex(x => !x.TrimStart(' ').StartsWith("using ")); - - Assert.Equal(2, splitOutput.Count(x => x.TrimStart(' ').StartsWith("using "))); - Assert.True(lastUsing < firsNotUsing); -======= - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - - Assert.Equal(1, splitOutput.Count(x => x.TrimStart(' ').StartsWith("using "))); - Assert.Equal(3, splitOutput.Length); - _fileSystem.Verify(x => x.ReadFileLines(It.Is(i => i == "\\script3.csx")), Times.Never()); ->>>>>>> e46d53e6a34d0ae5aadc5876ae63d9d8af3e278e - } - - [Fact] - public void UsingInCodeDoesNotCountAsUsingImport() - { - var file = new List -<<<<<<< HEAD - { - @"#load ""script4.csx""", - "", - "using System;", - "using System.IO;", - "Console.WriteLine();", - @"using (var stream = new MemoryStream) {", - @"//do stuff", - @"}" - }; - - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\file.csx"))).Returns(file.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\file.csx"); - - var splitOutput = output.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - var firstNonImportUsing = splitOutput.ToList().FindIndex(x => x.TrimStart(' ').StartsWith("using ") && !x.Contains(";")); - var firstCodeLine = splitOutput.ToList().FindIndex(x => x.Contains("Console")); - - Assert.True(firstNonImportUsing > firstCodeLine); - } - - [Fact] - public void ShouldAddLineDirectiveRightAfterLastLoadIsIncludedInEachFile() - { - // f1 has usings and then loads - var f1 = new List - { - "using System;", - "using System.Diagnostics;", - @"#load ""C:\f2.csx"";", - @"#load ""C:\f3.csx"";", - @"Console.WriteLine(""First line of f1"");", - }; - - // f2 has no usings and multiple loads - var f2 = new List - { - @"#load ""C:\f4.csx"";", - @"#load ""C:\f5.csx"";", - @"Console.WriteLine(""First line of f2"");", - }; - - // f3 has usings and no loads - var f3 = new List - { - @"using System;", - @"using System.Diagnostics;", - @"Console.WriteLine(""First line of f3"");", - }; - - // f4 has no usings and no loads - var f4 = new List - { - @"Console.WriteLine(""First line of f4"");", - }; - - // f5 is no special case, just used to be loaded - var f5 = new List - { - @"using System;", - @"Console.WriteLine(""First line of f5"");", - }; - - _fileSystem.SetupGet(fs => fs.NewLine).Returns(Environment.NewLine); - _fileSystem.Setup(fs => fs.ReadFileLines(@"C:\f1.csx")) - .Returns(f1.ToArray()); - _fileSystem.Setup(fs => fs.ReadFileLines(@"C:\f2.csx")) - .Returns(f2.ToArray()).Verifiable(); - _fileSystem.Setup(fs => fs.ReadFileLines(@"C:\f3.csx")) - .Returns(f3.ToArray()); - _fileSystem.Setup(fs => fs.ReadFileLines(@"C:\f4.csx")) - .Returns(f4.ToArray()); - _fileSystem.Setup(fs => fs.ReadFileLines(@"C:\f5.csx")) - .Returns(f5.ToArray()); - _fileSystem.Setup(fs => fs.IsPathRooted(It.IsAny())).Returns(true); - - var preProcessor = new FilePreProcessor(_fileSystem.Object); - - var file = preProcessor.ProcessFile(@"C:\f1.csx"); - - var fileLines = file.Split(new[]{ Environment.NewLine }, StringSplitOptions.None); - - // using statements go first, after that f4 -> f5 -> f2 -> f3 -> f1 - var line = 0; - fileLines[line++].ShouldEqual("using System;"); - fileLines[line++].ShouldEqual("using System.Diagnostics;"); - - fileLines[line++].ShouldEqual(@"#line 1 ""C:\f4.csx"""); - fileLines[line++].ShouldEqual(f4[0]); - - fileLines[line++].ShouldEqual(@"#line 2 ""C:\f5.csx"""); - fileLines[line++].ShouldEqual(f5[1]); - - fileLines[line++].ShouldEqual(@"#line 3 ""C:\f2.csx"""); - fileLines[line++].ShouldEqual(f2[2]); - - fileLines[line++].ShouldEqual(@"#line 3 ""C:\f3.csx"""); - fileLines[line++].ShouldEqual(f3[2]); - - fileLines[line++].ShouldEqual(@"#line 5 ""C:\f1.csx"""); - fileLines[line].ShouldEqual(f1[4]); -======= - { - @"#load ""script4.csx""", - "", - "using System;", - "using System.IO;", - "Console.WriteLine();", - @"using (var stream = new MemoryStream) {", - @"//do stuff", - @"}" - }; - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\file.csx"))).Returns(file.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\file.csx"); - - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - var firstNonImportUsing = - splitOutput.ToList().FindIndex(x => x.TrimStart(' ').StartsWith("using ") && !x.Contains(";")); - var firstCodeLine = splitOutput.ToList().FindIndex(x => x.Contains("Console")); - - Assert.True(firstNonImportUsing > firstCodeLine); - } - - [Fact] - public void ShouldHaveReferencesOnTop() - { - var file1 = new List - { - @"#r ""My.dll""", - @"#load ""script2.csx""", - "using System;", - @"Console.WriteLine(""Hi!"");" - }; - - var fs = new Mock(); - fs.Setup(i => i.NewLine).Returns(Environment.NewLine); - fs.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script1.csx"))).Returns(file1.ToArray()); - fs.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script2.csx"))).Returns(_file2.ToArray()); - - var processor = new FilePreProcessor(fs.Object); - var output = processor.ProcessFile("\\script1.csx"); - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None).ToList(); - - var lastR = splitOutput.FindLastIndex(line => line.StartsWith("#r ")); - var firstNotR = splitOutput.FindIndex(line => !line.StartsWith("#r ")); - - lastR.ShouldNotEqual(-1); - Assert.True(lastR < firstNotR); - } - - [Fact] - public void ShouldHaveReferencesFromAllFiles() - { - var file1 = new List - { - @"#r ""My.dll""", - @"#load ""scriptX.csx""", - "using System;", - @"Console.WriteLine(""Hi!"");" - }; - - var file2 = new List - { - @"#r ""My2.dll""", - "using System;", - @"Console.WriteLine(""Hi!"");" - }; - - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\script1.csx"))) - .Returns(file1.ToArray()); - _fileSystem.Setup(x => x.ReadFileLines(It.Is(f => f == "\\scriptX.csx"))) - .Returns(file2.ToArray()); - - var processor = new FilePreProcessor(_fileSystem.Object); - var output = processor.ProcessFile("\\script1.csx"); - - var splitOutput = output.Split(new[] {Environment.NewLine}, StringSplitOptions.None); - splitOutput.Count(line => line.StartsWith("#r ")).ShouldEqual(2); ->>>>>>> e46d53e6a34d0ae5aadc5876ae63d9d8af3e278e - } - } - } -} diff --git a/test/ScriptCs.Core.Tests/FileSystemMigratorTests.cs b/test/ScriptCs.Core.Tests/FileSystemMigratorTests.cs deleted file mode 100644 index 34a11151..00000000 --- a/test/ScriptCs.Core.Tests/FileSystemMigratorTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Moq; -using Ploeh.AutoFixture.Xunit; -using ScriptCs.Contracts; -using ScriptCs.Logging; -using Xunit.Extensions; - -namespace ScriptCs.Tests -{ - public class FileSystemMigratorTests - { - public class TheMigrateMethod - { - [Theory] - [ScriptCsAutoData("scriptcs_bin")] - [ScriptCsAutoData(".scriptcs_cache")] - [ScriptCsAutoData("scriptcs_packages")] - public void DoesNotMigrateWhenACurrentDirectoryIsFound( - string fileName, [Frozen] Mock fileSystem) - { - // arrange - SetupUnmigrated(fileSystem); - fileSystem.Setup(f => f.DirectoryExists(fileName)).Returns(true); - - var sut = new FileSystemMigrator(fileSystem.Object, new Mock().Object); - - // act - sut.Migrate(); - - // assert - VerifyNoMigration(fileSystem); - } - - [Theory] - [ScriptCsAutoData("scriptcs_packages.config")] - [ScriptCsAutoData("scriptcs_nuget.config")] - public void DoesNotMigrateWhenACurrentFileIsFound( - string fileName, [Frozen] Mock fileSystem) - { - // arrange - SetupUnmigrated(fileSystem); - fileSystem.Setup(f => f.FileExists(fileName)).Returns(true); - - var sut = new FileSystemMigrator(fileSystem.Object, new Mock().Object); - - // act - sut.Migrate(); - - // assert - VerifyNoMigration(fileSystem); - } - - private static void SetupUnmigrated(Mock fileSystem) - { - fileSystem.Setup(f => f.FileExists(It.IsAny())).Returns(true); - fileSystem.Setup(f => f.DirectoryExists(fileSystem.Object.BinFolder)).Returns(false); - fileSystem.Setup(f => f.DirectoryExists(fileSystem.Object.DllCacheFolder)).Returns(false); - fileSystem.Setup(f => f.FileExists(fileSystem.Object.NugetFile)).Returns(false); - fileSystem.Setup(f => f.FileExists(fileSystem.Object.PackagesFile)).Returns(false); - fileSystem.Setup(f => f.DirectoryExists(fileSystem.Object.PackagesFolder)).Returns(false); - } - - private static void VerifyNoMigration(Mock fileSystem) - { - fileSystem.Verify(f => - f.CopyDirectory(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - - fileSystem.Verify(f => - f.MoveDirectory(It.IsAny(), It.IsAny()), Times.Never); - - fileSystem.Verify(f => - f.Copy(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - - fileSystem.Verify(f => - f.Move(It.IsAny(), It.IsAny()), Times.Never); - } - } - } -} diff --git a/test/ScriptCs.Core.Tests/LoadLineProcessorTests.cs b/test/ScriptCs.Core.Tests/LoadLineProcessorTests.cs index 9950ea58..6a37a696 100644 --- a/test/ScriptCs.Core.Tests/LoadLineProcessorTests.cs +++ b/test/ScriptCs.Core.Tests/LoadLineProcessorTests.cs @@ -1,16 +1,11 @@ using System; - using Moq; - -using Ploeh.AutoFixture.Xunit; - using ScriptCs.Contracts; - using Should; - -using Xunit.Extensions; using Should.Core.Assertions; using ScriptCs.Contracts.Exceptions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -69,7 +64,7 @@ public void ShouldThrowExceptionIfAfterCode( fileSystem.Setup(x => x.GetFullPath(RelativePath)).Returns(FullPath); // Act / Assert - Assert.Throws(typeof(InvalidDirectiveUseException), () => processor.ProcessLine(parser.Object, context, Line, false)); + Xunit.Assert.Throws(() => processor.ProcessLine(parser.Object, context, Line, false)); } [Theory, ScriptCsAutoData] diff --git a/test/ScriptCs.Core.Tests/PackageAssemblyResolverTests.cs b/test/ScriptCs.Core.Tests/PackageAssemblyResolverTests.cs index a17733ae..89ad9385 100644 --- a/test/ScriptCs.Core.Tests/PackageAssemblyResolverTests.cs +++ b/test/ScriptCs.Core.Tests/PackageAssemblyResolverTests.cs @@ -6,8 +6,7 @@ using Moq; using NuGet; using ScriptCs.Contracts; -using ScriptCs.Logging; -using Should; +using Should; using Xunit; using IFileSystem = ScriptCs.Contracts.IFileSystem; @@ -17,7 +16,7 @@ public class PackageAssemblyResolverTests { public class GetAssemblyNamesMethod { - private readonly TestLogger _log = new TestLogger(); + private readonly TestLogProvider _logProvider=new TestLogProvider(); private readonly Mock _filesystem; private readonly Mock _package; private readonly Mock _packageContainer; @@ -63,7 +62,7 @@ public GetAssemblyNamesMethod() [Fact] public void WhenManyPackagesAreMatchedAllMatchingDllsWithUniquePathsShouldBeReturned() { - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); _packageIds.Add(new PackageReference("testId2", VersionUtility.ParseFrameworkName("net40"), new Version("3.0"))); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -86,7 +85,7 @@ public void WhenManyPackagesAreMatchedAllMatchingDllsShouldBeReturned() _packageContainer.Setup(i => i.FindPackage(It.IsAny(), It.Is(x => x.PackageId == "testId2"))).Returns(p.Object); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); found.ShouldNotBeEmpty(); @@ -102,7 +101,7 @@ public void WhenPackageIsMatchedItsNonMatchingDllsShouldBeExcluded() It.Is(x => x.FullName == VersionUtility.ParseFrameworkName("net40").FullName))) .Returns(new List { "test.dll" }); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -130,7 +129,7 @@ public void WhenManyPackagesAreMatchedAllNonMatchingDllsShouldBeExcluded() _packageContainer.Setup(i => i.FindPackage(It.IsAny(), It.Is(x => x.PackageId == "testId2"))).Returns(p.Object); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -141,7 +140,7 @@ public void WhenManyPackagesAreMatchedAllNonMatchingDllsShouldBeExcluded() [Fact] public void WhenDllsAreMatchedDllFilePathsAreCorrectlyConcatenated() { - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory); @@ -158,13 +157,13 @@ public void WhenNoPackagesAreFoundShouldLogWarning() _packageContainer.Setup(i => i.FindPackage(It.IsAny(), It.IsAny())) .Returns>(null); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); // act resolver.GetAssemblyNames(_workingDirectory); // assert - _log.Output.ShouldContain("WARN: Cannot find: testId 3.0"); + _logProvider.Output.ShouldContain("WARN: Cannot find: testId 3.0"); } [Fact] @@ -173,20 +172,20 @@ public void WhenPackagesAreFoundButNoMatchingDllsExistShouldLogWarning() // arrange _package.Setup(i => i.GetCompatibleDlls(It.IsAny())).Returns>(null); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); // act resolver.GetAssemblyNames(_workingDirectory); // assert - _log.Output.ShouldContain( + _logProvider.Output.ShouldContain( "WARN: Cannot find compatible binaries for .NETFramework,Version=v4.0 in: testId 3.0"); } [Fact] public void WhenPackageDirectoryDoesNotExistShouldReturnEmptyPackagesList() { - var resolver = new PackageAssemblyResolver(_filesystem.Object, new Mock().Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, new Mock().Object, _logProvider, _assemblyUtility.Object); _filesystem.Setup(i => i.DirectoryExists(It.IsAny())).Returns(false); var found = resolver.GetAssemblyNames(_workingDirectory); @@ -196,7 +195,7 @@ public void WhenPackageDirectoryDoesNotExistShouldReturnEmptyPackagesList() [Fact] public void WhenPackagesConfigDoesNotExistShouldReturnEmptyPackagesList() { - var resolver = new PackageAssemblyResolver(_filesystem.Object, new Mock().Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, new Mock().Object, _logProvider, _assemblyUtility.Object); _filesystem.Setup(i => i.FileExists(It.IsAny())).Returns(false); var found = resolver.GetAssemblyNames(_workingDirectory); @@ -219,7 +218,7 @@ public void ShouldLoadAllDependenciesIfPackageHasAny() i => i.FindPackage(It.IsAny(), It.Is(x => x.PackageId == "p2"))) .Returns(p.Object); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -246,7 +245,7 @@ public void ShouldNotLoadDuplicateDependencies() i => i.FindPackage(It.IsAny(), It.Is(x => x.PackageId == "p2"))) .Returns(p.Object); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -271,7 +270,7 @@ public void ShouldNotLoadIncompatibleDependenciesDlls() i => i.FindPackage(It.IsAny(), It.Is(x => x.PackageId == "p2"))) .Returns(p.Object); - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); var found = resolver.GetAssemblyNames(_workingDirectory).ToList(); @@ -283,7 +282,7 @@ public void ShouldNotLoadIncompatibleDependenciesDlls() [Fact] public void ShouldIgnoreUnmanagedAssemblies() { - var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _log, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_filesystem.Object, _packageContainer.Object, _logProvider, _assemblyUtility.Object); _packageIds.Add(new PackageReference("testId2", VersionUtility.ParseFrameworkName("net40"), new Version("3.0"))); _assemblyUtility.Setup(u => u.IsManagedAssembly(It.Is(s => Path.GetFileName(s) == "test.dll"))).Returns(false); @@ -298,7 +297,7 @@ public class GetPackagesMethod { private Mock _fs; private Mock _pc; - private Mock _logger; + private TestLogProvider _logProvider = new TestLogProvider(); private Mock _assemblyUtility; public GetPackagesMethod() @@ -308,7 +307,6 @@ public GetPackagesMethod() _fs.SetupGet(f => f.PackagesFile).Returns("packages.config"); _pc = new Mock(); - _logger = new Mock(); _assemblyUtility = new Mock(); } @@ -317,7 +315,7 @@ public void ShouldReturnEmptyIfPackagesConfigDoesNotExist() { _fs.Setup(i => i.FileExists(It.IsAny())).Returns(false); - var resolver = new PackageAssemblyResolver(_fs.Object, _pc.Object, _logger.Object, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_fs.Object, _pc.Object, _logProvider, _assemblyUtility.Object); var result = resolver.GetPackages(@"c:/"); result.ShouldBeEmpty(); @@ -329,7 +327,7 @@ public void ShouldGetReferencesToPackages() _fs.Setup(i => i.FileExists(It.IsAny())).Returns(true); _pc.Setup(i => i.FindReferences(It.IsAny())).Returns(new List { new PackageReference("id", VersionUtility.ParseFrameworkName("net40"), new Version("3.0")) }); - var resolver = new PackageAssemblyResolver(_fs.Object, _pc.Object, _logger.Object, _assemblyUtility.Object); + var resolver = new PackageAssemblyResolver(_fs.Object, _pc.Object, _logProvider, _assemblyUtility.Object); var result = resolver.GetPackages(@"c:/"); _pc.Verify(i => i.FindReferences(It.IsAny()), Times.Once()); @@ -341,7 +339,7 @@ public class SavePackagesMethod { private Mock _fs; private Mock _pc; - private readonly TestLogger _log = new TestLogger(); + private readonly TestLogProvider _logProvider = new TestLogProvider(); private Mock _assemblyUtility; public SavePackagesMethod() @@ -356,7 +354,7 @@ public SavePackagesMethod() private IPackageAssemblyResolver GetResolver() { - return new PackageAssemblyResolver(_fs.Object, _pc.Object, _log, _assemblyUtility.Object); + return new PackageAssemblyResolver(_fs.Object, _pc.Object, _logProvider, _assemblyUtility.Object); } [Fact] @@ -370,7 +368,7 @@ public void ShouldNotSaveWhenThereIsNoPackagesFolder() resolver.SavePackages(); _pc.Verify(i => i.CreatePackageFile(), Times.Never()); - _log.Output.ShouldContain("WARN: Packages directory does not exist!"); + _logProvider.Output.ShouldContain("WARN: Packages directory does not exist!"); } [Fact] diff --git a/test/ScriptCs.Core.Tests/PackageReferenceTests.cs b/test/ScriptCs.Core.Tests/PackageReferenceTests.cs index dd6a0bc2..89c044b9 100644 --- a/test/ScriptCs.Core.Tests/PackageReferenceTests.cs +++ b/test/ScriptCs.Core.Tests/PackageReferenceTests.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Versioning; using Should; -using Xunit.Extensions; +using Xunit; namespace ScriptCs.Tests { diff --git a/test/ScriptCs.Core.Tests/Properties/AssemblyInfo.cs b/test/ScriptCs.Core.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index febbab00..00000000 --- a/test/ScriptCs.Core.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("ScriptCs.Core.Tests")] -[assembly: AssemblyDescription("")] - -[assembly: Guid("fe718b7f-5fb4-43eb-a3da-26a1d25e644c")] diff --git a/test/ScriptCs.Core.Tests/ReferenceLineProcessorTests.cs b/test/ScriptCs.Core.Tests/ReferenceLineProcessorTests.cs index 7f0755a2..cdf606a4 100644 --- a/test/ScriptCs.Core.Tests/ReferenceLineProcessorTests.cs +++ b/test/ScriptCs.Core.Tests/ReferenceLineProcessorTests.cs @@ -1,12 +1,12 @@ using System; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; using Should; -using Xunit.Extensions; using System.Linq; using Should.Core.Assertions; using ScriptCs.Contracts.Exceptions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -63,9 +63,9 @@ public void ShouldThrowExceptionIfAfterCode( const string FullPath = "C:\\script.csx"; fileSystem.Setup(x => x.GetFullPath(RelativePath)).Returns(FullPath); - + // Act / Assert - Assert.Throws(typeof(InvalidDirectiveUseException), () => processor.ProcessLine(parser, context, Line, false)); + Xunit.Assert.Throws(() => processor.ProcessLine(parser, context, Line, false)); } [Theory, ScriptCsAutoData] diff --git a/test/ScriptCs.Core.Tests/ReplCommands/AliasCommandTests.cs b/test/ScriptCs.Core.Tests/ReplCommands/AliasCommandTests.cs index 9bf6518f..7b6203c8 100644 --- a/test/ScriptCs.Core.Tests/ReplCommands/AliasCommandTests.cs +++ b/test/ScriptCs.Core.Tests/ReplCommands/AliasCommandTests.cs @@ -3,7 +3,6 @@ using Moq; using ScriptCs.Contracts; using ScriptCs.ReplCommands; -using ScriptCs.Logging; using Should; using Xunit; using Xunit.Extensions; @@ -32,7 +31,7 @@ public void ShouldAliasCommandWithNewName( Mock fileSystem, Mock engine, Mock serializer, - Mock logger, + TestLogProvider logProvider, Mock composer, Mock console, Mock filePreProcessor) @@ -50,11 +49,13 @@ public void ShouldAliasCommandWithNewName( fileSystem.Object, engine.Object, serializer.Object, - logger.Object, + logProvider, composer.Object, console.Object, filePreProcessor.Object, - new List { dummyCommand.Object }); + new List { dummyCommand.Object }, + new Printers(serializer.Object), + new ScriptInfo()); var cmd = new AliasCommand(console.Object); diff --git a/test/ScriptCs.Core.Tests/ReplCommands/ExitCommandTests.cs b/test/ScriptCs.Core.Tests/ReplCommands/ExitCommandTests.cs index 56d621a2..f4e77b88 100644 --- a/test/ScriptCs.Core.Tests/ReplCommands/ExitCommandTests.cs +++ b/test/ScriptCs.Core.Tests/ReplCommands/ExitCommandTests.cs @@ -22,13 +22,14 @@ public void ReturnsExit() public class ExecuteMethod { + private const string message = "Are you sure you wish to exit? (y/n):"; + [Fact] public void PromptsUserBeforeExiting() { // arrange - const string message = "Are you sure you wish to exit? (y/n): "; var console = new Mock(); - console.Setup(x => x.ReadLine()).Returns("n"); + console.Setup(x => x.ReadLine(message)).Returns("n"); var executor = new Mock(); var cmd = new ExitCommand(console.Object); @@ -36,7 +37,7 @@ public void PromptsUserBeforeExiting() cmd.Execute(executor.Object, null); // assert - console.Verify(x => x.Write(message)); + console.Verify(x => x.ReadLine(message)); } [Fact] @@ -44,7 +45,7 @@ public void ExitsWhenUserAnswersYes() { // arrange var console = new Mock(); - console.Setup(x => x.ReadLine()).Returns("y"); + console.Setup(x => x.ReadLine(message)).Returns("y"); var executor = new Mock(); var cmd = new ExitCommand(console.Object); @@ -61,7 +62,7 @@ public void DoesNotExitWhenUserAnswersNo() { // arrange var console = new Mock(); - console.Setup(x => x.ReadLine()).Returns("n"); + console.Setup(x => x.ReadLine(message)).Returns("n"); var executor = new Mock(); var cmd = new ExitCommand(console.Object); diff --git a/test/ScriptCs.Core.Tests/ReplCommands/HelpCommandTests.cs b/test/ScriptCs.Core.Tests/ReplCommands/HelpCommandTests.cs index f2b813d5..2cbbecf2 100644 --- a/test/ScriptCs.Core.Tests/ReplCommands/HelpCommandTests.cs +++ b/test/ScriptCs.Core.Tests/ReplCommands/HelpCommandTests.cs @@ -28,6 +28,8 @@ public void PrintsCommandsToConsole() { // arrange var console = new Mock(); + console.Setup(c => c.Width).Returns(80); + var repl = new Mock(); var clearCommand = new ClearCommand(console.Object); var exitCommand = new ExitCommand(console.Object); @@ -46,9 +48,9 @@ public void PrintsCommandsToConsole() cmd.Execute(repl.Object, null); // assert - console.Verify(x => x.WriteLine(It.IsAny()), Times.Exactly(3)); - console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(":" + clearCommand.CommandName) && f.Contains(clearCommand.Description))), Times.Once); - console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(":" + exitCommand.CommandName) && f.Contains(exitCommand.Description))), Times.Once); + console.Verify(x => x.WriteLine(It.IsAny()), Times.Exactly(4)); + console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(" :" + clearCommand.CommandName) && f.Contains(clearCommand.Description))), Times.Once); + console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(" :" + exitCommand.CommandName) && f.Contains(exitCommand.Description))), Times.Once); } [Fact] @@ -56,6 +58,8 @@ public void CorrectlyPrintsCommandsToConsoleAfterAlias() { // arrange var console = new Mock(); + console.Setup(c => c.Width).Returns(80); + var repl = new Mock(); var clearCommand = new ClearCommand(console.Object); var aliasCommand = new AliasCommand(console.Object); @@ -75,9 +79,11 @@ public void CorrectlyPrintsCommandsToConsoleAfterAlias() cmd.Execute(repl.Object, null); // assert - console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(":" + clearCommand.CommandName) && f.Contains(clearCommand.Description))), Times.Once); - console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(":" + aliasCommand.CommandName) && f.Contains(aliasCommand.Description))), Times.Once); - console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(":clr") && f.Contains(clearCommand.Description))), Times.Once); + // because we now have formatted wrapping with the description, we have to remove + // all the extra spaces before verifying + console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(" :" + clearCommand.CommandName) && f.Replace(" ", " ").Contains(clearCommand.Description))), Times.Once); + console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(" :" + aliasCommand.CommandName) && f.Replace(" ", " ").Contains(aliasCommand.Description))), Times.Once); + console.Verify(x => x.WriteLine(It.Is(f => f.StartsWith(" :clr") && f.Replace(" ", " ").Contains(clearCommand.Description))), Times.Once); } } } diff --git a/test/ScriptCs.Core.Tests/ReplCommands/InstallCommandTests.cs b/test/ScriptCs.Core.Tests/ReplCommands/InstallCommandTests.cs index 47bd84b6..5e1322a4 100644 --- a/test/ScriptCs.Core.Tests/ReplCommands/InstallCommandTests.cs +++ b/test/ScriptCs.Core.Tests/ReplCommands/InstallCommandTests.cs @@ -4,7 +4,6 @@ using Moq; using ScriptCs.Contracts; using ScriptCs.ReplCommands; -using ScriptCs.Logging; using Xunit; using Xunit.Extensions; @@ -21,7 +20,7 @@ public void ReturnsInstall() var cmd = new InstallCommand( new Mock().Object, new Mock().Object, - new Mock().Object, + new TestLogProvider(), new Mock().Object); // assert @@ -33,7 +32,7 @@ public class ExecuteMethod { private readonly Mock _packageInstaller; private readonly Mock _packageAssemblyResolver; - private readonly Mock _logger; + private readonly TestLogProvider _logProvider = new TestLogProvider(); private readonly Mock _installationProvider; private readonly Mock _repl; @@ -41,7 +40,6 @@ public ExecuteMethod() { _packageInstaller = new Mock(); _packageAssemblyResolver = new Mock(); - _logger = new Mock(); _installationProvider = new Mock(); _repl = new Mock(); @@ -54,7 +52,7 @@ public ExecuteMethod() private InstallCommand GetCommand() { - return new InstallCommand(_packageInstaller.Object, _packageAssemblyResolver.Object, _logger.Object, _installationProvider.Object); + return new InstallCommand(_packageInstaller.Object, _packageAssemblyResolver.Object, _logProvider, _installationProvider.Object); } [Fact] diff --git a/test/ScriptCs.Core.Tests/ReplTests.cs b/test/ScriptCs.Core.Tests/ReplTests.cs index fe0504cf..8c8422b4 100644 --- a/test/ScriptCs.Core.Tests/ReplTests.cs +++ b/test/ScriptCs.Core.Tests/ReplTests.cs @@ -4,12 +4,10 @@ using System.Linq; using Moq; using Moq.Protected; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; -using ScriptCs.Logging; using Should; using Xunit; -using Xunit.Extensions; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -25,7 +23,7 @@ public Mocks() FileSystem.SetupGet(x => x.DllCacheFolder).Returns(".cache"); FileSystem.SetupGet(x => x.PackagesFolder).Returns("scriptcs_packages"); ScriptEngine = new Mock(); - Logger = new Mock(); + LogProvider = new TestLogProvider(); ScriptLibraryComposer = new Mock(); ScriptLibraryComposer.SetupGet(p => p.ScriptLibrariesFile).Returns("PackageScripts.csx"); Console = new Mock(); @@ -41,7 +39,7 @@ public Mocks() public Mock ScriptEngine { get; private set; } - public Mock Logger { get; private set; } + public TestLogProvider LogProvider { get; private set; } public Mock Console { get; private set; } @@ -61,11 +59,13 @@ public static Repl GetRepl(Mocks mocks) mocks.FileSystem.Object, mocks.ScriptEngine.Object, mocks.ObjectSerializer.Object, - mocks.Logger.Object, + mocks.LogProvider, mocks.ScriptLibraryComposer.Object, mocks.Console.Object, mocks.FilePreProcessor.Object, - mocks.ReplCommands.Select(x => x.Object)); + mocks.ReplCommands.Select(x => x.Object), + new Printers(mocks.ObjectSerializer.Object), + new ScriptInfo()); } public class TheConstructor @@ -77,7 +77,6 @@ public void ShouldProperlyInitializeMembers() var repl = GetRepl(mocks); repl.FileSystem.ShouldEqual(mocks.FileSystem.Object); repl.ScriptEngine.ShouldEqual(mocks.ScriptEngine.Object); - repl.Logger.ShouldEqual(mocks.Logger.Object); repl.Console.ShouldEqual(mocks.Console.Object); } } @@ -189,7 +188,7 @@ public void CallsExecuteOnTheScriptEngine() { _mocks.ScriptEngine.Verify( x => x.Execute( - "foo", + "Env.Initialize();" + Environment.NewLine + "foo", new string[0], It.Is(i => i.Assemblies.SequenceEqual(_repl.References.Assemblies)), It.Is>(i => i.SequenceEqual(_repl.Namespaces)), @@ -389,8 +388,8 @@ public void ShouldReferenceAssemblyIfLineIsAReference() _repl.Initialize(Enumerable.Empty(), Enumerable.Empty()); _repl.Execute("#r \"my.dll\""); - //default references = 9, + 1 we just added - _repl.References.Paths.Count().ShouldEqual(10); + //default references = 10, + 1 we just added + _repl.References.Paths.Count().ShouldEqual(11); } [Fact] @@ -483,14 +482,19 @@ public void ShouldNotExecuteAnythingIfLineIsAReference() } [Fact] - public void ShouldSetBufferIFLineIsFirstOfMultilineConstruct() + public void ShouldSetBufferIfLineIsFirstOfMultilineConstruct() { var mocks = new Mocks(); mocks.ScriptEngine.Setup( x => - x.Execute(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Returns(x => ScriptResult.Incomplete); + x.Execute( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Returns(ScriptResult.Incomplete); + mocks.FilePreProcessor.Setup(x => x.ProcessScript(It.IsAny())) .Returns(new FilePreProcessorResult { Code = "var x = 1;" }); mocks.FileSystem.Setup(i => i.CurrentDirectory).Returns("C:/"); @@ -507,8 +511,12 @@ public void ShouldResetBufferIfLineIsNoLongerMultilineConstruct() var mocks = new Mocks(); mocks.ScriptEngine.Setup( x => - x.Execute(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) + x.Execute( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) .Returns(ScriptResult.Empty); mocks.FilePreProcessor.Setup(x => x.ProcessScript(It.IsAny())) .Returns(new FilePreProcessorResult { Code = "}" }); diff --git a/test/ScriptCs.Core.Tests/ScriptCs.Core.Tests.csproj b/test/ScriptCs.Core.Tests/ScriptCs.Core.Tests.csproj index 381fad26..322ee903 100644 --- a/test/ScriptCs.Core.Tests/ScriptCs.Core.Tests.csproj +++ b/test/ScriptCs.Core.Tests/ScriptCs.Core.Tests.csproj @@ -1,143 +1,27 @@ - - - + - Debug - AnyCPU - {AC228213-7356-4F0D-BA48-EBA5FB8A7506} - Library - ScriptCs.Tests - ScriptCs.Core.Tests - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset - 1701 + net461 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - ScriptCsAutoDataAttribute.cs - - - ScriptCsMoqCustomization.cs - - - TestLogger.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + + - - False - ..\..\packages\Microsoft.Web.Xdt.2.1.0\lib\net40\Microsoft.Web.XmlTransform.dll - - - ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - False - ..\..\packages\Nuget.Core.2.8.5\lib\net40-Client\NuGet.Core.dll - - - ..\..\packages\AutoFixture.3.18.8\lib\net40\Ploeh.AutoFixture.dll - - - ..\..\packages\AutoFixture.AutoMoq.3.18.8\lib\net40\Ploeh.AutoFixture.AutoMoq.dll - - - ..\..\packages\AutoFixture.Xunit.3.18.8\lib\net40\Ploeh.AutoFixture.Xunit.dll - - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - + + + + + + + + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Core.Tests/ScriptExecutorTests.cs b/test/ScriptCs.Core.Tests/ScriptExecutorTests.cs index 52334b10..7f79667d 100644 --- a/test/ScriptCs.Core.Tests/ScriptExecutorTests.cs +++ b/test/ScriptCs.Core.Tests/ScriptExecutorTests.cs @@ -1,16 +1,13 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Security.Policy; using Moq; using Moq.Protected; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; -using ScriptCs.Logging; using Should; using Xunit; -using Xunit.Extensions; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -106,6 +103,47 @@ public void ShouldTerminateScriptPacksWhenTerminateIsCalled( public class TheEngineExecuteMethod { + [Theory, ScriptCsAutoData] + public void ShouldSetScriptInfo_ScriptPath( + [Frozen] Mock scriptEngine, + ScriptExecutor scriptExecutor) + { + scriptEngine.Setup(e => e.Execute( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())); + + scriptExecutor.Initialize(Enumerable.Empty(), Enumerable.Empty()); + + var result = new FilePreProcessorResult(); + result.ScriptPath = "Main.csx"; + scriptExecutor.EngineExecute("", new string[] {}, result); + scriptExecutor.ScriptInfo.ScriptPath.ShouldEqual("Main.csx"); + } + + [Theory, ScriptCsAutoData] + public void ShouldPopulateScriptInfo_LoadedScript( + [Frozen] Mock scriptEngine, + ScriptExecutor scriptExecutor) + { + scriptEngine.Setup(e => e.Execute( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())); + + scriptExecutor.Initialize(Enumerable.Empty(), Enumerable.Empty()); + + var result = new FilePreProcessorResult(); + result.ScriptPath = "Main.csx"; + result.LoadedScripts.Add("Child.csx"); + scriptExecutor.EngineExecute("", new string[] { }, result); + scriptExecutor.ScriptInfo.LoadedScripts.First().ShouldEqual("Child.csx"); + } + [Theory, ScriptCsAutoData] public void ShouldAddReferenceToEachDestinationFile( [Frozen] Mock scriptEngine, @@ -157,15 +195,8 @@ public void ShouldAddReferenceToEachDestinationFile( scriptExecutor.EngineExecute("", new string[] {}, new FilePreProcessorResult()); // assert - scriptEngine.Verify( - e => e.Execute( - It.IsAny(), - It.IsAny(), - It.Is(x => x.Paths - .SequenceEqual(defaultReferences.Union(explicitReferences.Union(destPaths)))), - It.IsAny>(), - It.IsAny()), - Times.Once()); + var expectedReferencePaths = explicitReferences.Union(destPaths).Distinct(); + expectedReferencePaths.All(path => scriptExecutor.References.Paths.Contains(path)).ShouldBeTrue(); } [Theory, ScriptCsAutoData] @@ -191,7 +222,7 @@ public void ShouldExecuteScriptReturnedFromFileProcessorInScriptEngine( scriptEngine.Verify( s => s.Execute( - code, + "Env.Initialize();" + Environment.NewLine + code, It.IsAny(), It.IsAny(), It.IsAny>(), @@ -526,7 +557,7 @@ public TheInjectScriptLibrariesMethod() public void ShouldExitIfPreProcessorResultIsNull(ScriptExecutor executor) { executor.InjectScriptLibraries("", _result, _state); - _result.Code.ShouldBeEmpty(); + _result.Code.ShouldEqual("Env.Initialize();" + Environment.NewLine); } [Theory, ScriptCsAutoData] @@ -545,7 +576,7 @@ public void ShouldInjectResultCode(Mock executor) executor.Protected(); executor.Setup(e => e.LoadScriptLibraries(It.IsAny())).Returns(_scriptLibrariesPreProcessorResult); executor.Object.InjectScriptLibraries("", _result, _state); - _result.Code.ShouldEqual("Test" + Environment.NewLine); + _result.Code.ShouldEqual("Test" + Environment.NewLine + "Env.Initialize();" + Environment.NewLine); } [Theory, ScriptCsAutoData] @@ -585,13 +616,13 @@ public void ShouldPreProcessTheScriptLibrariesFileIfPresent( [Frozen] Mock fileSystem, [Frozen] Mock preProcessor, [Frozen] Mock engine, - [Frozen] Mock logger, + [Frozen] TestLogProvider logProvider, [Frozen] Mock composer) { // arrange fileSystem.Setup(fs => fs.FileExists(It.IsAny())).Returns(true); var executor = new ScriptExecutor( - fileSystem.Object, preProcessor.Object, engine.Object, logger.Object,composer.Object); + fileSystem.Object, preProcessor.Object, engine.Object, logProvider, composer.Object, new ScriptInfo()); // act executor.LoadScriptLibraries(""); @@ -618,7 +649,6 @@ public void ShouldAddReferenceToEachAssembly(ScriptExecutor executor) executor.References.Assemblies.ShouldContain(assembly1); executor.References.Assemblies.ShouldContain(assembly2); executor.References.Assemblies.ShouldContain(assembly3); - executor.References.Assemblies.Count().ShouldEqual(3); } } @@ -640,7 +670,6 @@ public void ShouldRemoveReferenceToEachAssembly(ScriptExecutor executor) executor.References.Assemblies.ShouldNotContain(assembly1); executor.References.Assemblies.ShouldNotContain(assembly2); executor.References.Assemblies.ShouldContain(assembly3); - executor.References.Assemblies.Count().ShouldEqual(1); } } } diff --git a/test/ScriptCs.Core.Tests/ScriptHostTests.cs b/test/ScriptCs.Core.Tests/ScriptHostTests.cs index 9be473c3..a90e169a 100644 --- a/test/ScriptCs.Core.Tests/ScriptHostTests.cs +++ b/test/ScriptCs.Core.Tests/ScriptHostTests.cs @@ -14,11 +14,14 @@ public class TheGetMethod { private readonly Mock _mockContext = new Mock(); private readonly Mock _mockScriptPackManager = new Mock(); - private readonly ScriptHost _scriptHost; + private readonly Mock _mockConsole = new Mock(); + private readonly Mock _mockSerializer = new Mock(); + + private readonly ScriptHost _scriptHost; public TheGetMethod() { - _scriptHost = new ScriptHost(_mockScriptPackManager.Object, new ScriptEnvironment(new string[0])); + _scriptHost = new ScriptHost(_mockScriptPackManager.Object, new ScriptEnvironment(new string[0], _mockConsole.Object, new Printers(_mockSerializer.Object))); _mockScriptPackManager.Setup(s => s.Get()).Returns(_mockContext.Object); } @@ -32,10 +35,13 @@ public void ShoulGetScriptPackFromScriptPackManagerWhenInvoked() public class TheConstructor { + private readonly Mock _mockConsole = new Mock(); + private readonly Mock _mockSerializer = new Mock(); + [Fact] public void ShouldSetScriptEnvironment() { - var environment = new ScriptEnvironment(new string[0]); + var environment = new ScriptEnvironment(new string[0], _mockConsole.Object, new Printers(_mockSerializer.Object)); var scriptHost = new ScriptHost(new Mock().Object, environment); scriptHost.Env.ShouldEqual(environment); diff --git a/test/ScriptCs.Core.Tests/ScriptLibraryComposerTests.cs b/test/ScriptCs.Core.Tests/ScriptLibraryComposerTests.cs index 63d2c913..7818f2bb 100644 --- a/test/ScriptCs.Core.Tests/ScriptLibraryComposerTests.cs +++ b/test/ScriptCs.Core.Tests/ScriptLibraryComposerTests.cs @@ -3,12 +3,11 @@ using System.Linq; using System.Text; using ScriptCs.Contracts; -using ScriptCs.Logging; using Moq; using Moq.Protected; -using Ploeh.AutoFixture.Xunit; -using Xunit.Extensions; using Should; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -44,18 +43,17 @@ public void ShouldFindThePackage( [Theory, ScriptCsAutoData] public void ShouldLogAWarningIfThePackageIsMissing( - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, [Frozen] Mock packageContainer, ScriptLibraryComposer composer, - Mock reference, - Mock package) + Mock reference) { packageContainer.Setup(c => c.FindPackage(It.IsAny(), It.IsAny())) .Returns((IPackageObject)null); reference.SetupGet(r => r.PackageId).Returns("test"); composer.ProcessPackage("", reference.Object, new StringBuilder(), new List(), new List()); packageContainer.Verify(c => c.FindPackage(It.IsAny(), It.IsAny())); - ((TestLogger)log).Output.ShouldContain("WARN: Package missing: test"); + logProvider.Output.ShouldContain("WARN: Package missing: test"); } @@ -80,7 +78,7 @@ public void ShouldPreProcessTheScriptFile( public void ShouldWarnIfMultipleMainFilesArePresent( [Frozen] Mock packageContainer, [Frozen] Mock preProcessor, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, ScriptLibraryComposer composer, Mock reference, Mock package) @@ -91,7 +89,7 @@ public void ShouldWarnIfMultipleMainFilesArePresent( package.Setup(p => p.GetContentFiles()).Returns(new List { "Test1Main.csx", "Test2Main.csx" }); preProcessor.Setup(p => p.ProcessFile(It.IsAny())).Returns(new FilePreProcessorResult()); composer.ProcessPackage(@"c:\packages", reference.Object, new StringBuilder(), new List(), new List()); - ((TestLogger) log).Output.ShouldContain( + logProvider.Output.ShouldContain( "WARN: Script Libraries in 'Test' ignored due to multiple Main files being present"); } diff --git a/test/ScriptCs.Core.Tests/ShebangLineProcessorTests.cs b/test/ScriptCs.Core.Tests/ShebangLineProcessorTests.cs index 626e2a34..7b8ff5a3 100644 --- a/test/ScriptCs.Core.Tests/ShebangLineProcessorTests.cs +++ b/test/ScriptCs.Core.Tests/ShebangLineProcessorTests.cs @@ -1,6 +1,7 @@ using Should; using Xunit.Extensions; using ScriptCs.Contracts; +using Xunit; namespace ScriptCs.Tests { diff --git a/test/ScriptCs.Core.Tests/UsingLineProcessorTests.cs b/test/ScriptCs.Core.Tests/UsingLineProcessorTests.cs index 16132826..59cfc05b 100644 --- a/test/ScriptCs.Core.Tests/UsingLineProcessorTests.cs +++ b/test/ScriptCs.Core.Tests/UsingLineProcessorTests.cs @@ -1,8 +1,6 @@ using ScriptCs.Contracts; - using Should; - -using Xunit.Extensions; +using Xunit; namespace ScriptCs.Tests { @@ -14,7 +12,7 @@ public class TheProcessLineMethod public void ShouldReturnTrueOnUsingLine(IFileParser parser, UsingLineProcessor processor) { // Arrange - const string UsingLine = @"using ""System.Data"";"; + const string UsingLine = "using System.Data;"; // Act var result = processor.ProcessLine(parser, new FileParserContext(), UsingLine, true); @@ -40,7 +38,21 @@ public void ShouldReturnFalseOtherwise(IFileParser parser, UsingLineProcessor pr public void ShouldIgnoreAliases(IFileParser parser, UsingLineProcessor processor) { // Arrange - const string UsingLine = @"using Path = ""System.IO.Path"";"; + const string UsingLine = "using Path = System.IO.Path"; + + // Act + var result = processor.ProcessLine(parser, new FileParserContext(), UsingLine, true); + + // Assert + result.ShouldBeFalse(); + } + + [Theory, ScriptCsAutoData] + public void ShouldIgnoreUsingStatic(IFileParser parser, UsingLineProcessor processor) + { + // Arrange + const string UsingLine = "using static System.Console;"; + var context = new FileParserContext(); // Act var result = processor.ProcessLine(parser, new FileParserContext(), UsingLine, true); @@ -53,7 +65,7 @@ public void ShouldIgnoreAliases(IFileParser parser, UsingLineProcessor processor public void ShouldAddNamespaceToContext(IFileParser parser, UsingLineProcessor processor) { // Arrange - const string UsingLine = @"using ""System.Data"";"; + const string UsingLine = "using System.Data;"; var context = new FileParserContext(); // Act diff --git a/test/ScriptCs.Core.Tests/app.config b/test/ScriptCs.Core.Tests/app.config index 445a99c4..31080f45 100644 --- a/test/ScriptCs.Core.Tests/app.config +++ b/test/ScriptCs.Core.Tests/app.config @@ -7,9 +7,9 @@ - - + + - \ No newline at end of file + diff --git a/test/ScriptCs.Core.Tests/packages.config b/test/ScriptCs.Core.Tests/packages.config deleted file mode 100644 index 0ed2ff85..00000000 --- a/test/ScriptCs.Core.Tests/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Engine.Mono.Tests/MonoModuleTest.cs b/test/ScriptCs.Engine.Mono.Tests/MonoModuleTest.cs deleted file mode 100644 index 40cd616c..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/MonoModuleTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using Moq; -using ScriptCs.Contracts; -using Should; -using Xunit; - -namespace ScriptCs.Engine.Mono.Tests -{ - public class MonoModuleTest - { - public class TheInitializeMethod - { - private readonly Mock _configMock = new Mock(); - private readonly IModuleConfiguration _config; - private readonly MonoModule _module = new MonoModule(); - private readonly IDictionary _overrides = new Dictionary(); - - public TheInitializeMethod() - { - _configMock.SetupGet(c => c.Debug).Returns(false); - _configMock.SetupGet(c => c.IsRepl).Returns(false); - _configMock.SetupGet(c => c.Cache).Returns(false); - _configMock.SetupGet(c => c.Overrides).Returns(_overrides); - _config = _configMock.Object; - } - - - [Fact] - public void ShouldNotOverrideTheEngineIfOneIsRegistered() - { - var engine = new Mock(); - _overrides[typeof(IScriptEngine)] = engine.Object; - _module.Initialize(_config); - _overrides[typeof(IScriptEngine)].ShouldEqual(engine.Object); - } - - [Fact] - public void ShouldRegisterTheEngine() - { - _module.Initialize(_config); - _configMock.Verify(c=>c.ScriptEngine(), Times.Once); - } - } - } -} diff --git a/test/ScriptCs.Engine.Mono.Tests/MonoScriptEngineTests.cs b/test/ScriptCs.Engine.Mono.Tests/MonoScriptEngineTests.cs deleted file mode 100644 index 6928fff6..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/MonoScriptEngineTests.cs +++ /dev/null @@ -1,363 +0,0 @@ -extern alias MonoCSharp; - -using System.Collections.ObjectModel; -using System.Linq; -using MonoCSharp::Mono.CSharp; -using Moq; -using Ploeh.AutoFixture.Xunit; -using ScriptCs.Contracts; -using ScriptCs.Logging; -using ScriptCs.Tests; -using Should; -using Xunit.Extensions; - -namespace ScriptCs.Engine.Mono.Tests -{ - public class MonoScriptEngineTests - { - public class GetLocalVariablesMethod - { - [Theory, ScriptCsAutoData] - public void ShouldReturnDeclaredVariables([NoAutoProperties]MonoScriptEngine engine, ScriptPackSession scriptPackSession) - { - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - - engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), - scriptPackSession); - engine.Execute(@"var y = ""www"";", new string[0], new AssemblyReferences(), Enumerable.Empty(), - scriptPackSession); - - engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { "int x = 1", @"string y = ""www""" }); - } - - [Theory, ScriptCsAutoData] - public void ShouldReturnOnlyLastValueOfVariablesDeclaredManyTimes([NoAutoProperties]MonoScriptEngine engine, ScriptPackSession scriptPackSession) - { - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - - engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - engine.Execute("int x = 2;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - - engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { "int x = 2" }); - } - - [Theory, ScriptCsAutoData] - public void ShouldReturn0VariablesAfterReset([NoAutoProperties]MonoScriptEngine engine, ScriptPackSession scriptPackSession) - { - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - - engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), - scriptPackSession); - engine.Execute(@"var y = ""www"";", new string[0], new AssemblyReferences(), Enumerable.Empty(), - scriptPackSession); - - scriptPackSession.State[MonoScriptEngine.SessionKey] = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - - engine.GetLocalVariables(scriptPackSession).ShouldBeEmpty(); - } - } - - public class TheExecuteMethod - { - [Theory, ScriptCsAutoData] - public void ShouldCreateScriptHostWithContexts( - [Frozen] Mock scriptHostFactory, - [Frozen] Mock scriptPack, - ScriptPackSession scriptPackSession, - [NoAutoProperties] MonoScriptEngine engine) - { - // Arrange - const string Code = "var a = 0;"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - scriptPack.Setup(p => p.Initialize(It.IsAny())); - scriptPack.Setup(p => p.GetContext()).Returns((IScriptPackContext)null); - - // Act - engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - - // Assert - scriptHostFactory.Verify(f => f.CreateScriptHost(It.IsAny(), It.IsAny())); - } - - [Theory, ScriptCsAutoData] - public void ShouldReuseExistingSessionIfProvided( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoTestScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var a = 0;"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - - // Act - engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - - // Assert - engine.Session.ShouldEqual(session.Session); - } - - [Theory, ScriptCsAutoData] - public void ShouldCreateNewSessionIfNotProvided( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoTestScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var a = 0;"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - // Act - engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - - // Assert - engine.Session.ShouldNotBeNull(); - } - - [Theory, ScriptCsAutoData] - public void ShouldAddNewReferencesIfTheyAreProvided( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoTestScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var a = 0;"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - ((SessionState)scriptPackSession.State[MonoScriptEngine.SessionKey]).References.Paths.Count().ShouldEqual(1); - } - - [Theory, ScriptCsAutoData] - public void ShouldReturnAScriptResult( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - var code = string.Empty; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ShouldBeType(); - } - - // Need to get the compiler ex from mono. - // Currently the ConsoleReportWriter is swallowing the ex - /* - [Theory, ScriptCsAutoData] - public void ShouldReturnCompileExceptionIfCodeDoesNotCompile( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "this shold not compile"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext( new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(); - refs.PathReferences.Add("System"); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - System.Threading.Thread.Sleep(1000); - - // Assert - result.CompileExceptionInfo.ShouldNotBeNull(); - } - */ - - [Theory, ScriptCsAutoData] - public void ShouldNotReturnCompileExceptionIfCodeDoesCompile( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var theNumber = 42; //this should compile"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ShouldBeType(); - result.CompileExceptionInfo.ShouldBeNull(); - result.ExecuteExceptionInfo.ShouldBeNull(); - } - - [Theory, ScriptCsAutoData] - public void ShouldReturnExecuteExceptionIfCodeExecutionThrowsException( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "throw new System.Exception(); //this should throw an Exception"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ExecuteExceptionInfo.ShouldNotBeNull(); - } - - [Theory, ScriptCsAutoData] - public void ShouldNotReturnExecuteExceptionIfCodeExecutionDoesNotThrowAnException( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var theNumber = 42; //this should not throw an Exception"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ExecuteExceptionInfo.ShouldBeNull(); - } - - [Theory, ScriptCsAutoData] - public void ShouldReturnReturnValueIfCodeExecutionReturnsValue( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Mono doesn't support comments after evals as well as Roslyn - const string Code = "\"Hello\""; //this should return \"Hello\""; - - // Arrange - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ReturnValue.ShouldEqual("Hello"); - } - - [Theory, ScriptCsAutoData] - public void ShouldNotReturnReturnValueIfCodeExecutionDoesNotReturnValue( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var theNumber = 42; //this should not return a value"; - - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - - var session = new SessionState { Session = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter())) }; - scriptPackSession.State[MonoScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); - - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - result.ReturnValue.ShouldBeNull(); - } - - [Theory, ScriptCsAutoData] - public void ShouldInitializeScriptLibraryWrapperHost( - [Frozen] Mock scriptHostFactory, - Mock manager, - [NoAutoProperties] MonoScriptEngine engine, - ScriptPackSession scriptPackSession - ) - { - // Arrange - const string Code = "var theNumber = 42; //this should compile"; - - var refs = new AssemblyReferences(new[] { "System" }); - - scriptHostFactory.Setup(s => s.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns(new ScriptHost(manager.Object, null)); - - // Act - engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - - // Assert - ScriptLibraryWrapper.ScriptHost.ShouldNotEqual(null); - } - } - - public class MonoTestScriptEngine : MonoScriptEngine - { - public MonoTestScriptEngine(IScriptHostFactory scriptHostFactory, ILog logger) - : base(scriptHostFactory, logger) - { - } - - public Evaluator Session { get; private set; } - - protected override ScriptResult Execute(string code, Evaluator session) - { - Session = session; - return ScriptResult.Empty; - } - } - } -} diff --git a/test/ScriptCs.Engine.Mono.Tests/Properties/AssemblyInfo.cs b/test/ScriptCs.Engine.Mono.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 0bcca9d5..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ScriptCs.Engine.Mono.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ScriptCs.Engine.Mono.Tests")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4f23561c-7948-45b6-89ef-facb2228f001")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/ScriptCs.Engine.Mono.Tests/ScriptCs.Engine.Mono.Tests.csproj b/test/ScriptCs.Engine.Mono.Tests/ScriptCs.Engine.Mono.Tests.csproj deleted file mode 100644 index ccaf1a20..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/ScriptCs.Engine.Mono.Tests.csproj +++ /dev/null @@ -1,114 +0,0 @@ - - - - - Debug - AnyCPU - {D0723C2D-D3B4-40B7-8E5A-84FB3A6E5092} - Library - ScriptCs.Engine.Mono.Tests - ScriptCs.Engine.Mono.Tests - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset - 1701 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ScriptCsAutoDataAttribute.cs - - - ScriptCsMoqCustomization.cs - - - TestLogger.cs - - - - - - - - - - - - - - - - - - - False - ..\..\packages\Mono.CSharp.4.0.0.143\lib\4.5\Mono.CSharp.dll - MonoCSharp - - - ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - ..\..\packages\AutoFixture.3.18.8\lib\net40\Ploeh.AutoFixture.dll - - - ..\..\packages\AutoFixture.AutoMoq.3.18.8\lib\net40\Ploeh.AutoFixture.AutoMoq.dll - - - ..\..\packages\AutoFixture.Xunit.3.18.8\lib\net40\Ploeh.AutoFixture.Xunit.dll - - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - - - - - {6049E205-8B5F-4080-B023-70600E51FD64} - ScriptCs.Contracts - - - {E590E710-E159-48E6-A3E6-1A83D3FE732C} - ScriptCs.Core - - - {E4ADCFEE-FF3B-4EF5-8298-2B31F407F58B} - ScriptCs.Engine.Mono - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Engine.Mono.Tests/Segmenter/CodeAnalyserTests.cs b/test/ScriptCs.Engine.Mono.Tests/Segmenter/CodeAnalyserTests.cs deleted file mode 100644 index 15d10857..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/Segmenter/CodeAnalyserTests.cs +++ /dev/null @@ -1,105 +0,0 @@ -using ScriptCs.Engine.Mono.Segmenter.Analyser; -using Should; -using Xunit; - -namespace ScriptCs.Engine.Mono.Tests.Segmenter -{ - public class CodeAnalyserTests - { - public class AnalyseSegments - { - [Fact] - public void ShouldReturnTrueIfIsClass() - { - const string Code = "class A { }"; - - var analyser = new CodeAnalyzer(); - analyser.IsClass(Code).ShouldBeTrue(); - } - - [Fact] - public void ShouldReturnFalseIfIsNotClass() - { - const string Code = "void Bar() { }"; - - var analyser = new CodeAnalyzer(); - analyser.IsClass(Code).ShouldBeFalse(); - } - - [Fact] - public void ShouldReturnFalseIfIsNotMethod() - { - const string Code = "class A { } "; - - var rewriter = new CodeAnalyzer(); - rewriter.IsMethod(Code).ShouldBeFalse(); - } - - [Fact] - public void ShouldReturnFalseIfIncompeteMethod() - { - const string Code = "void Bar() { "; - - var analyser = new CodeAnalyzer(); - analyser.IsMethod(Code).ShouldBeFalse(); - } - - [Fact] - public void ShouldReturnFalseIfMissingMethodBody() - { - const string Code = "void Bar()"; - - var analyser = new CodeAnalyzer(); - analyser.IsMethod(Code).ShouldBeFalse(); - } - - [Fact] - public void ShouldReturnTrueIfIsMethod() - { - const string Code = "void Bar() { }"; - - var analyser = new CodeAnalyzer(); - analyser.IsMethod(Code).ShouldBeTrue(); - } - } - - public class MethodRewrites - { - [Fact] - public void ShouldRewriteToPrototypeAndExpression() - { - const string Code = "void Bar() { }"; - - var analyser = new CodeAnalyzer(); - var method = analyser.ExtractPrototypeAndMethod(Code); - - method.ProtoType.ShouldEqual("Action Bar;"); - method.MethodExpression.ShouldEqual("Bar = delegate () { };"); - } - - [Fact] - public void ShouldPreserveMethodBody() - { - const string Code = "int Foo(int a) { Foo();\n\nreturn a;\n}"; - - var rewriter = new CodeAnalyzer(); - var method = rewriter.ExtractPrototypeAndMethod(Code); - - method.ProtoType.ShouldEqual("Func Foo;"); - method.MethodExpression.ShouldEqual("Foo = delegate (int a) { Foo();\n\nreturn a;\n};"); - } - - [Fact] - public void ShouldPreserveLineCountInMethodSignature() - { - const string Code = "int\nFoo\n(\n)\n { return 42; }"; - - var rewriter = new CodeAnalyzer(); - var method = rewriter.ExtractPrototypeAndMethod(Code); - - method.ProtoType.ShouldEqual("Func Foo;\n\n\n\n"); - method.MethodExpression.ShouldEqual("\n\n\n\nFoo = delegate () { return 42; };"); - } - } - } -} \ No newline at end of file diff --git a/test/ScriptCs.Engine.Mono.Tests/Segmenter/RegionParserTests.cs b/test/ScriptCs.Engine.Mono.Tests/Segmenter/RegionParserTests.cs deleted file mode 100644 index 00df817d..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/Segmenter/RegionParserTests.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System.Linq; -using ScriptCs.Engine.Mono.Segmenter.Parser; -using Should; -using Xunit; - -namespace ScriptCs.Engine.Mono.Tests.Segmenter -{ - public class RegionParserTests - { - public class ParseStatements - { - [Fact] - public void ShouldExtractEmptyStatement() - { - const string Code = ";"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractVariableStatements() - { - const string Code = "var x = 10;"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - // Bug #713 - [Fact] - public void ShouldExtractMultipleStatements() - { - const string Code = "var x = 123;Action a = () => x++;Console.WriteLine(x);"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(3); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual("var x = 123;"); - - region = result[1]; - Code.Substring(region.Offset, region.Length).ShouldEqual("Action a = () => x++;"); - - region = result[2]; - Code.Substring(region.Offset, region.Length).ShouldEqual("Console.WriteLine(x);"); - } - - [Fact] - public void ShouldExtractComplexVariableStatement() - { - const string Code = "var version = File.ReadAllText(\"src/CommonAssemblyInfo.cs\").Split(new[] { \"AssemblyInformationalVersion(\\\"\" }, 2, StringSplitOptions.None).ElementAt(1).Split(new[] { '\"' }).First();"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractComplexMethods() - { - const string Code = "var bau = Require();\n\nbau\n.Task(\"default\").DependsOn(string.IsNullOrWhiteSpace(ci) ? new[] { \"unit\", \"component\", \"pack\" } : new[] { \"unit\", \"component\", \"accept\", \"pack\" });\n"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(2); - - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual("var bau = Require();"); - - region = result[1]; - Code.Substring(region.Offset, region.Length).ShouldEqual("bau\n.Task(\"default\").DependsOn(string.IsNullOrWhiteSpace(ci) ? new[] { \"unit\", \"component\", \"pack\" } : new[] { \"unit\", \"component\", \"accept\", \"pack\" });"); - } - } - - public class ParseBlocks - { - [Fact] - public void ShouldExtractBlock() - { - const string Code = "if (true) { some code; }"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractMultipleBlocks() - { - const string Code = "using(var s = File.Open(\"test.cs\")){ } public Foo(int x) { return x }"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(2); - - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual("using(var s = File.Open(\"test.cs\")){ }"); - - region = result[1]; - Code.Substring(region.Offset, region.Length).ShouldEqual("public Foo(int x) { return x }"); - } - - [Fact] - public void ShouldExtractBlockCodeThatBeginsWithLeftCurlyBrackets() - { - const string Code = "{ Foo(); ) }"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractForLoops() // for loops have ';' - { - const string Code = "for(var i = 0; i < 3; i++) { Foo(); }"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractDoWhileAsSingleBlock() - { - const string Code = "do { } while (true);"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - - [Fact] - public void ShouldExtractInvalidDoWhileAsTwoBlocks() - { - const string Code = "do { } if (true);"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(2); - - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual("do { }"); - - region = result[1]; - Code.Substring(region.Offset, region.Length).ShouldEqual("if (true);"); - } - } - - public class ParseExpressions - { - [Fact] - public void ShouldExtractExpressionsInParantheses() - { - const string Code = "(10 + 5 * ( 4 - 8) )"; - - var parser = new RegionParser(); - var result = parser.Parse(Code); - - result.Count().ShouldEqual(1); - var region = result[0]; - Code.Substring(region.Offset, region.Length).ShouldEqual(Code); - } - } - } -} \ No newline at end of file diff --git a/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptLexerTests.cs b/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptLexerTests.cs deleted file mode 100644 index 2dedc1f1..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptLexerTests.cs +++ /dev/null @@ -1,232 +0,0 @@ -using ScriptCs.Engine.Mono.Segmenter.Lexer; -using Should; -using Xunit; - -namespace ScriptCs.Engine.Mono.Tests.Segmenter -{ - public class ScriptLexerTests - { - public class TheLexer - { - [Fact] - public void ShouldIdentifyIdentifiersAsToken() - { - const string Code = " id "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.Identifier); - token.TokenValue.ShouldEqual("id"); - } - - [Fact] - public void ShouldIdentifySemiColonAsToken() - { - const string Code = " ; "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.SemiColon); - } - - [Fact] - public void ShouldIdentifyLeftBracketAsToken() - { - const string Code = " { "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.LeftBracket); - } - - [Fact] - public void ShouldIdentifyRightBracketAsToken() - { - const string Code = " } "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.RightBracket); - } - - [Fact] - public void ShouldIdentifyLeftParentheseAsToken() - { - const string Code = " ( "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.LeftParenthese); - } - - [Fact] - public void ShouldIdentifyRightParentheseAsToken() - { - const string Code = " ) "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.RightParenthese); - } - - [Fact] - public void ShouldIdentifyStringsAsToken() - { - const string Code = "\"This is a string\""; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.String); - token.Start.ShouldEqual(0); - token.End.ShouldEqual(18); - token.TokenValue.ShouldEqual(Code); - } - - [Fact] - public void ShouldIdentifyEmptyStrings() - { - const string Code = "\"\""; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.String); - token.Start.ShouldEqual(0); - token.End.ShouldEqual(2); - token.TokenValue.ShouldEqual(Code); - } - - [Fact] - public void ShouldIdentifyStringsWithEscapeChars() - { - const string Code = "\"AssemblyInformationalVersion(\\\"\""; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.String); - token.TokenValue.ShouldEqual(Code); - } - - [Fact] - public void ShouldIdenityCharactersAsToken() - { - const string Code = "\'A\'"; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.Character); - token.Start.ShouldEqual(0); - token.End.ShouldEqual(3); - token.TokenValue.ShouldEqual(Code); - } - - [Fact] - public void ShouldNotFailOnIdentifyingCharactersAsToken() - { - const string Code = "\'A"; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.Character); - token.Start.ShouldEqual(0); - token.End.ShouldEqual(2); - token.TokenValue.ShouldEqual(Code); - } - - [Fact] - public void ShouldIdentifyUnlexedAsThemselves() - { - const string Code = " < "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual('<'); - } - - [Fact] - public void ShouldRemoveSingleLineComments() - { - const string Code = " { // This is removed \n } "; - - var lexer = new ScriptLexer(Code); - - var token = lexer.GetToken(); - token.Token.ShouldEqual('{'); - - token = lexer.GetToken(); - token.Token.ShouldEqual('}'); - - token = lexer.GetToken(); - token.Token.ShouldEqual(Token.Eof); - } - - [Fact] - public void ShouldRemoveMultiLineComments() - { - const string Code = " { /* This is \n removed */ } "; - - var lexer = new ScriptLexer(Code); - - var token = lexer.GetToken(); - token.Token.ShouldEqual('{'); - - token = lexer.GetToken(); - token.Token.ShouldEqual('}'); - - token = lexer.GetToken(); - token.Token.ShouldEqual(Token.Eof); - } - - [Fact] - public void ShouldReturnTokenRegion() - { - const string Code = " id "; - - var lexer = new ScriptLexer(Code); - var result = lexer.GetToken(); - - result.Start.ShouldEqual(1); - result.End.ShouldEqual(3); - - result = lexer.GetToken(); //< - result.Start.ShouldEqual(3); - result.End.ShouldEqual(4); - } - - [Fact] - public void ShouldIdentifyDoAsToken() - { - const string Code = "do { } while(true); "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.Do); - token.TokenValue.ShouldEqual("do"); - } - - [Fact] - public void ShouldIdentifyWhileAsToken() - { - const string Code = "while(true); "; - - var lexer = new ScriptLexer(Code); - var token = lexer.GetToken(); - - token.Token.ShouldEqual(Token.While); - token.TokenValue.ShouldEqual("while"); - } - } - } -} diff --git a/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptSegmenterTests.cs b/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptSegmenterTests.cs deleted file mode 100644 index 676772c7..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/Segmenter/ScriptSegmenterTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Linq; -using Should; -using Xunit; - -namespace ScriptCs.Engine.Mono.Tests.Segmenter -{ - using ScriptCs.Engine.Mono.Segmenter; - - public class ScriptSegmenterTests - { - public class SegmentCode - { - [Fact] - public void ShouldSegmentCodeAndReturnInCorrectOrder() - { - const string Code = "void Bar() {} Bar(); class A {}"; - - var segmenter = new ScriptSegmenter(); - - var result = segmenter.Segment(Code); - - result.Count().ShouldEqual(4); - - result[0].Type.ShouldEqual(SegmentType.Class); - result[0].Code.ShouldEqual("class A {}"); - result[1].Type.ShouldEqual(SegmentType.Prototype); - result[1].Code.ShouldEqual("Action Bar;"); - result[2].Type.ShouldEqual(SegmentType.Method); - result[2].Code.ShouldEqual("Bar = delegate () {};"); - result[3].Type.ShouldEqual(SegmentType.Evaluation); - result[3].Code.ShouldEqual("Bar();"); - } - } - } -} \ No newline at end of file diff --git a/test/ScriptCs.Engine.Mono.Tests/app.config b/test/ScriptCs.Engine.Mono.Tests/app.config deleted file mode 100644 index 288bbd3e..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Engine.Mono.Tests/packages.config b/test/ScriptCs.Engine.Mono.Tests/packages.config deleted file mode 100644 index 849ecb44..00000000 --- a/test/ScriptCs.Engine.Mono.Tests/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Engine.Roslyn.Tests/RoslynModuleTests.cs b/test/ScriptCs.Engine.Roslyn.Tests/CSharpModuleTests.cs similarity index 93% rename from test/ScriptCs.Engine.Roslyn.Tests/RoslynModuleTests.cs rename to test/ScriptCs.Engine.Roslyn.Tests/CSharpModuleTests.cs index 2dcd689f..d10fed92 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/RoslynModuleTests.cs +++ b/test/ScriptCs.Engine.Roslyn.Tests/CSharpModuleTests.cs @@ -3,12 +3,12 @@ using Moq; using ScriptCs.Contracts; using ScriptCs.Engine.Roslyn; -using Xunit; using Should; +using Xunit; namespace ScriptCs.Tests { - public class RoslynModuleTests + public class CSharpModuleTests { public class TheInitializeMethod { @@ -40,14 +40,14 @@ public void ShouldRegisterThePersistantScriptEngineWhenCacheIsEnabled() { _configMock.SetupGet(c => c.Cache).Returns(true); _module.Initialize(_config); - _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(RoslynScriptPersistentEngine)); + _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(CSharpPersistentEngine)); } [Fact] public void ShouldRegisterTheScriptEngineWhenCacheIsDisabled() { _module.Initialize(_config); - _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(RoslynScriptEngine)); + _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(CSharpScriptEngine)); } [Fact] @@ -55,7 +55,7 @@ public void ShouldRegisterTheInMemoryEngineWhenDebugIsEnabled() { _configMock.Setup(c => c.Debug).Returns(true); _module.Initialize(_config); - _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(RoslynScriptInMemoryEngine)); + _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(CSharpScriptInMemoryEngine)); } [Fact] @@ -63,7 +63,7 @@ public void ShouldRegisterTheReplEngineWhenReplIsEnabled() { _configMock.Setup(c => c.IsRepl).Returns(true); _module.Initialize(_config); - _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(RoslynReplEngine)); + _overrides[typeof(IScriptEngine)].ShouldEqual(typeof(CSharpReplEngine)); } } } diff --git a/test/ScriptCs.Engine.Roslyn.Tests/RoslynReplEngineTests.cs b/test/ScriptCs.Engine.Roslyn.Tests/CSharpReplEngineTests.cs similarity index 65% rename from test/ScriptCs.Engine.Roslyn.Tests/RoslynReplEngineTests.cs rename to test/ScriptCs.Engine.Roslyn.Tests/CSharpReplEngineTests.cs index d39dcb33..85a5b243 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/RoslynReplEngineTests.cs +++ b/test/ScriptCs.Engine.Roslyn.Tests/CSharpReplEngineTests.cs @@ -1,57 +1,57 @@ using System.Collections.ObjectModel; using System.Linq; -using Ploeh.AutoFixture.Xunit; -using Roslyn.Scripting; -using Roslyn.Scripting.CSharp; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; using ScriptCs.Contracts; using ScriptCs.Engine.Roslyn; using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { - public class RoslynReplEngineTests + public class CSharpReplEngineTests { public class GetLocalVariablesMethod { [Theory, ScriptCsAutoData] - public void ShouldReturnDeclaredVariables([NoAutoProperties]RoslynReplEngine engine, ScriptPackSession scriptPackSession) + public void ShouldReturnDeclaredVariables([NoAutoProperties]CSharpReplEngine engine, ScriptPackSession scriptPackSession) { - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); engine.Execute(@"var y = ""www"";", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { @"System.String y = www", "System.Int32 x = 1" }); + engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { "System.Int32 x", "System.String y" }); } [Theory, ScriptCsAutoData] - public void ShouldReturnOnlyLastValueOfVariablesDeclaredManyTimes([NoAutoProperties]RoslynReplEngine engine, ScriptPackSession scriptPackSession) + public void ShouldReturnOnlyLastValueOfVariablesDeclaredManyTimes([NoAutoProperties]CSharpReplEngine engine, ScriptPackSession scriptPackSession) { - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); engine.Execute("int x = 2;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { "System.Int32 x = 2" }); + engine.GetLocalVariables(scriptPackSession).ShouldEqual(new Collection { "System.Int32 x" }); } [Theory, ScriptCsAutoData] - public void ShouldReturn0VariablesAfterReset([NoAutoProperties]RoslynReplEngine engine, ScriptPackSession scriptPackSession) + public void ShouldReturn0VariablesAfterReset([NoAutoProperties]CSharpReplEngine engine, ScriptPackSession scriptPackSession) { - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; engine.Execute("int x = 1;", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); engine.Execute(@"var y = ""www"";", new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); - scriptPackSession.State[RoslynScriptEngine.SessionKey] = new SessionState { Session = new ScriptEngine().CreateSession() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; engine.GetLocalVariables(scriptPackSession).ShouldBeEmpty(); } @@ -61,13 +61,13 @@ public class TheExecuteMethod { [Theory, ScriptCsAutoData] public void ShouldSetIsCompleteSubmissionToFalseIfCodeIsMissingCurlyBracket( - [NoAutoProperties] RoslynReplEngine engine, ScriptPackSession scriptPackSession) + [NoAutoProperties] CSharpReplEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "class test {"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; var refs = new AssemblyReferences(new[] { "System" }); // Act @@ -80,14 +80,14 @@ public void ShouldSetIsCompleteSubmissionToFalseIfCodeIsMissingCurlyBracket( [Theory, ScriptCsAutoData] public void ShouldSetIsCompleteSubmissionToFalseIfCodeIsMissingParenthesis( - [NoAutoProperties] RoslynReplEngine engine, + [NoAutoProperties] CSharpReplEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "System.Diagnostics.Debug.WriteLine(\"a\""; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; var refs = new AssemblyReferences(new[] { "System" }); // Act @@ -99,14 +99,14 @@ public void ShouldSetIsCompleteSubmissionToFalseIfCodeIsMissingParenthesis( [Theory, ScriptCsAutoData] public void ShouldSetIsCompleteSubmissionToFalseIfCodeIsMissingSquareBracket( - [NoAutoProperties] RoslynReplEngine engine, + [NoAutoProperties] CSharpReplEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var x = new[1] { 1 }; var y = x[0"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; var refs = new AssemblyReferences(new[] { "System" }); // Act diff --git a/test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptEngineTests.cs b/test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptEngineTests.cs similarity index 52% rename from test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptEngineTests.cs rename to test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptEngineTests.cs index b5a65357..30dd958b 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptEngineTests.cs +++ b/test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptEngineTests.cs @@ -1,34 +1,41 @@ using System.Linq; using System.Reflection; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; using Moq; -using Ploeh.AutoFixture.Xunit; -using Roslyn.Compilers; -using Roslyn.Scripting; -using Roslyn.Scripting.CSharp; using ScriptCs.Contracts; using ScriptCs.Engine.Roslyn; -using ScriptCs.Logging; using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { - public class RoslynScriptEngineTests + public class CSharpScriptEngineTests { public class TheExecuteMethod { + private IConsole _console = new Mock().Object; + private IObjectSerializer _serializer = new Mock().Object; + private Printers _printers; + + public TheExecuteMethod() + { + _printers = new Printers(_serializer); + } + [Theory, ScriptCsAutoData] public void ShouldCreateScriptHostWithContexts( [Frozen] Mock scriptHostFactory, [Frozen] Mock scriptPack, ScriptPackSession scriptPackSession, - [NoAutoProperties] RoslynScriptEngine engine) + [NoAutoProperties] CSharpScriptEngine engine) { // Arrange const string Code = "var a = 0;"; scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); + .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q, _console, _printers))); scriptPack.Setup(p => p.Initialize(It.IsAny())); scriptPack.Setup(p => p.GetContext()).Returns((IScriptPackContext)null); @@ -43,17 +50,17 @@ public void ShouldCreateScriptHostWithContexts( [Theory, ScriptCsAutoData] public void ShouldReuseExistingSessionIfProvided( [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynTestScriptEngine engine, + [NoAutoProperties] CSharpTestScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var a = 0;"; scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); + .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q, _console, _printers))); - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; // Act engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); @@ -62,20 +69,19 @@ public void ShouldReuseExistingSessionIfProvided( engine.Session.ShouldEqual(session.Session); } - [Theory, ScriptCsAutoData] - public void ShouldCreateNewSessionIfNotProvided( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynTestScriptEngine engine, - ScriptPackSession scriptPackSession) + [Fact] + public void ShouldCreateNewSessionIfNotProvided() { + var scriptHostFactory = new Mock(); + scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) + .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q, _console, _printers))); + // Arrange + var engine = new CSharpTestScriptEngine(scriptHostFactory.Object, new TestLogProvider()); const string Code = "var a = 0;"; - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q))); - // Act - engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), scriptPackSession); + engine.Execute(Code, new string[0], new AssemblyReferences(), Enumerable.Empty(), new ScriptPackSession(new IScriptPack[0], new string[0])); // Assert engine.Session.ShouldNotBeNull(); @@ -83,36 +89,34 @@ public void ShouldCreateNewSessionIfNotProvided( [Theory, ScriptCsAutoData] public void ShouldAddNewReferencesIfTheyAreProvided( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynTestScriptEngine engine, + [NoAutoProperties] CSharpTestScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var a = 0;"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); // Assert - ((SessionState)scriptPackSession.State[RoslynScriptEngine.SessionKey]).References.Paths.Count().ShouldEqual(1); + ((SessionState)scriptPackSession.State[CommonScriptEngine.SessionKey]).References.Paths.Count().ShouldEqual(2); } [Theory, ScriptCsAutoData] public void ShouldReturnAScriptResult( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynTestScriptEngine engine, + [NoAutoProperties] CSharpTestScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange var code = string.Empty; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -123,16 +127,15 @@ public void ShouldReturnAScriptResult( [Theory, ScriptCsAutoData] public void ShouldReturnCompileExceptionIfCodeDoesNotCompile( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "this shold not compile"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -141,14 +144,13 @@ public void ShouldReturnCompileExceptionIfCodeDoesNotCompile( result.CompileExceptionInfo.ShouldNotBeNull(); } - [Theory, ScriptCsAutoData] + [Theory(Skip = "this feature is not supported in Roslyn 1.0.0-rc2: see https://github.com/dotnet/roslyn/issues/1012"), ScriptCsAutoData] public void ShouldReturnInvalidNamespacesIfCS0241Encountered( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; // Act var result = engine.Execute(string.Empty, new string[0], new AssemblyReferences(), new[] { "foo" }, scriptPackSession); @@ -158,39 +160,39 @@ public void ShouldReturnInvalidNamespacesIfCS0241Encountered( result.InvalidNamespaces.ShouldContain("foo"); } - [Theory, ScriptCsAutoData] - public void ShouldRemoveInvalidNamespacesFromSessionStateAndEngine( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, - ScriptPackSession scriptPackSession) - { - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - - // Act - engine.Execute(string.Empty, new string[0], new AssemblyReferences(), new[] { "System", "foo" }, scriptPackSession); - - // Assert - session.Namespaces.ShouldNotBeEmpty(); - session.Namespaces.ShouldNotContain("foo"); - var pendingNamespacesField = session.Session.GetType().GetField("pendingNamespaces", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); - var pendingNamespacesValue = (ReadOnlyArray)pendingNamespacesField.GetValue(session.Session); - pendingNamespacesValue.AsEnumerable().ShouldNotBeEmpty(); - pendingNamespacesValue.AsEnumerable().ShouldNotContain("foo"); - } + //todo: filip: this test will not even compile as it used to do reflection old roslyn assemblies! + //[Theory, ScriptCsAutoData] + //public void ShouldRemoveInvalidNamespacesFromSessionStateAndEngine( + // [Frozen] Mock scriptHostFactory, + // [NoAutoProperties] CSharpScriptEngine engine, + // ScriptPackSession scriptPackSession) + //{ + // var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + // scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + + // // Act + // engine.Execute(string.Empty, new string[0], new AssemblyReferences(), new[] { "System", "foo" }, scriptPackSession); + + // // Assert + // session.Namespaces.ShouldNotBeEmpty(); + // session.Namespaces.ShouldNotContain("foo"); + // var pendingNamespacesField = session.Session.GetType().GetField("pendingNamespaces", BindingFlags.Instance | BindingFlags.NonPublic); + // var pendingNamespacesValue = (ReadOnlyArray)pendingNamespacesField.GetValue(session.Session); + // pendingNamespacesValue.AsEnumerable().ShouldNotBeEmpty(); + // pendingNamespacesValue.AsEnumerable().ShouldNotContain("foo"); + //} [Theory, ScriptCsAutoData] public void ShouldNotReturnCompileExceptionIfCodeDoesCompile( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var theNumber = 42; //this should compile"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -201,16 +203,15 @@ public void ShouldNotReturnCompileExceptionIfCodeDoesCompile( [Theory, ScriptCsAutoData] public void ShouldReturnExecuteExceptionIfCodeExecutionThrowsException( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "throw new System.Exception(); //this should throw an Exception"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -221,16 +222,15 @@ public void ShouldReturnExecuteExceptionIfCodeExecutionThrowsException( [Theory, ScriptCsAutoData] public void ShouldNotReturnExecuteExceptionIfCodeExecutionDoesNotThrowAnException( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var theNumber = 42; //this should not throw an Exception"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -241,15 +241,14 @@ public void ShouldNotReturnExecuteExceptionIfCodeExecutionDoesNotThrowAnExceptio [Theory, ScriptCsAutoData] public void ShouldReturnReturnValueIfCodeExecutionReturnsValue( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { const string Code = "\"Hello\" //this should return \"Hello\""; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -260,16 +259,15 @@ public void ShouldReturnReturnValueIfCodeExecutionReturnsValue( [Theory, ScriptCsAutoData] public void ShouldNotReturnReturnValueIfCodeExecutionDoesNotReturnValue( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "var theNumber = 42; //this should not return a value"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); // Act var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); @@ -278,20 +276,17 @@ public void ShouldNotReturnReturnValueIfCodeExecutionDoesNotReturnValue( result.ReturnValue.ShouldBeNull(); } - - [Theory, ScriptCsAutoData] public void ShouldNotMarkSubmissionsAsIncompleteWhenRunningScript( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, + [NoAutoProperties] CSharpScriptEngine engine, ScriptPackSession scriptPackSession) { // Arrange const string Code = "class test {"; - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { "System" }); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); engine.FileName = "test.csx"; // Act @@ -302,75 +297,71 @@ public void ShouldNotMarkSubmissionsAsIncompleteWhenRunningScript( result.IsCompleteSubmission.ShouldBeTrue(); result.CompileExceptionInfo.ShouldNotBeNull(); } - } - [Theory, ScriptCsAutoData] - public void ShouldCompileWhenUsingClassesFromAPassedAssemblyInstance( - [Frozen] Mock scriptHostFactory, - [NoAutoProperties] RoslynScriptEngine engine, - ScriptPackSession scriptPackSession) - { - // Arrange - const string Code = "var x = new ScriptCs.Tests.TestMarkerClass();"; + [Theory, ScriptCsAutoData] + public void ShouldCompileWhenUsingClassesFromAPassedAssemblyInstance( + [Frozen] Mock scriptHostFactory, + [Frozen] ScriptPackSession scriptPackSession) + { + // Arrange + const string Code = "var x = new ScriptCs.Tests.TestMarkerClass();"; - scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns((p, q) => new ScriptHost(p, q)); + scriptHostFactory.Setup(f => f.CreateScriptHost(It.IsAny(), It.IsAny())) + .Returns((p, q) => new ScriptHost(p, new ScriptEnvironment(q, _console, _printers))); - var session = new SessionState { Session = new ScriptEngine().CreateSession() }; - scriptPackSession.State[RoslynScriptEngine.SessionKey] = session; - var refs = new AssemblyReferences(new[] { Assembly.GetExecutingAssembly() }, new[] { "System" }); + var engine = new CSharpScriptEngine(scriptHostFactory.Object, new TestLogProvider()); + var session = new SessionState { Session = CSharpScript.RunAsync("").GetAwaiter().GetResult() }; + scriptPackSession.State[CommonScriptEngine.SessionKey] = session; + var refs = new AssemblyReferences(new[] { Assembly.GetExecutingAssembly() }, new[] { "System", "System.Runtime" }); - // Act - var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); + // Act + var result = engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - // Assert - result.CompileExceptionInfo.ShouldBeNull(); - result.ExecuteExceptionInfo.ShouldBeNull(); - } + // Assert + result.CompileExceptionInfo.ShouldBeNull(); + result.ExecuteExceptionInfo.ShouldBeNull(); + } - [Theory, ScriptCsAutoData] - public void ShouldInitializeScriptLibraryWrapperHost( - [Frozen] Mock scriptHostFactory, - Mock manager, - [NoAutoProperties] RoslynScriptEngine engine, - ScriptPackSession scriptPackSession - ) - { - // Arrange - const string Code = "var theNumber = 42; //this should compile"; + [Theory, ScriptCsAutoData] + public void ShouldInitializeScriptLibraryWrapperHost( + [Frozen] Mock scriptHostFactory, + Mock manager, + [NoAutoProperties] CSharpScriptEngine engine, + ScriptPackSession scriptPackSession + ) + { + // Arrange + const string Code = "var theNumber = 42; //this should compile"; - var refs = new AssemblyReferences(new[] { "System" }); + var refs = new AssemblyReferences(new[] { "System", "System.Runtime" }); - scriptHostFactory.Setup(s => s.CreateScriptHost(It.IsAny(), It.IsAny())) - .Returns(new ScriptHost(manager.Object, null)); + scriptHostFactory.Setup(s => s.CreateScriptHost(It.IsAny(), It.IsAny())) + .Returns(new ScriptHost(manager.Object, null)); - // Act - engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); + // Act + engine.Execute(Code, new string[0], refs, Enumerable.Empty(), scriptPackSession); - // Assert - ScriptLibraryWrapper.ScriptHost.ShouldNotEqual(null); + // Assert + ScriptLibraryWrapper.ScriptHost.ShouldNotEqual(null); + } } - public class RoslynTestScriptEngine : RoslynScriptEngine + public class CSharpTestScriptEngine : CSharpScriptEngine { - public RoslynTestScriptEngine(IScriptHostFactory scriptHostFactory, ILog logger) - : base(scriptHostFactory, logger) + public CSharpTestScriptEngine(IScriptHostFactory scriptHostFactory, TestLogProvider logProvider) + : base(scriptHostFactory, logProvider) { } - public Session Session { get; private set; } + public ScriptState Session { get; private set; } - protected override ScriptResult Execute(string code, Session session) + protected override ScriptResult Execute(string code, object globals, SessionState sessionState) { - Session = session; + base.Execute(code, globals, sessionState); + Session = sessionState.Session; return ScriptResult.Empty; } - - internal ScriptEngine Engine - { - get { return ScriptEngine; } - } } } diff --git a/test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptInMemoryEngineTests.cs b/test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptInMemoryEngineTests.cs similarity index 70% rename from test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptInMemoryEngineTests.cs rename to test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptInMemoryEngineTests.cs index 461b9f0f..1de12db6 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/RoslynScriptInMemoryEngineTests.cs +++ b/test/ScriptCs.Engine.Roslyn.Tests/CSharpScriptInMemoryEngineTests.cs @@ -2,25 +2,31 @@ using System.Collections.Generic; using System.Linq; using Moq; -using Roslyn.Compilers; using ScriptCs.Contracts; using ScriptCs.Engine.Roslyn; using ScriptCs.Exceptions; -using ScriptCs.Logging; +using Should; using Xunit; namespace ScriptCs.Tests { - using Should; - - public class RoslynScriptInMemoryEngineTests + public class CSharpScriptInMemoryEngineTests { public class TheExecuteMethod { + private IConsole _console = new Mock().Object; + private IObjectSerializer _serializer = new Mock().Object; + private Printers _printers; + + public TheExecuteMethod() + { + _printers = new Printers(_serializer); + } + [Fact] public void ShouldExposeExceptionThrownByScriptWhenErrorOccurs() { - var scriptEngine = new RoslynScriptInMemoryEngine(new ScriptHostFactory(), new Mock().Object); + var scriptEngine = new CSharpScriptInMemoryEngine(new ScriptHostFactory(_console, _printers, new ScriptInfo()), new TestLogProvider()); // Arrange var lines = new List { @@ -44,12 +50,12 @@ public void ShouldExposeExceptionThrownByScriptWhenErrorOccurs() [Fact] public void ShouldExposeExceptionThrownByCompilation() { - var scriptEngine = new RoslynScriptInMemoryEngine(new ScriptHostFactory(), new Mock().Object); + var scriptEngine = new CSharpScriptInMemoryEngine(new ScriptHostFactory(_console, _printers, new ScriptInfo()), new TestLogProvider()); // Arrange var lines = new List { - "using Sysasdasdasdtem;" + "Sysasdasdasdtem;" }; var code = string.Join(Environment.NewLine, lines); @@ -61,8 +67,7 @@ public void ShouldExposeExceptionThrownByCompilation() // Assert var exception = Assert.Throws(() => result.CompileExceptionInfo.Throw()); - exception.InnerException.ShouldBeType(); - exception.Message.ShouldContain("The type or namespace name 'Sysasdasdasdtem' could not be found"); + exception.Message.ShouldContain("error CS0103: The name 'Sysasdasdasdtem' does not exist in the current context"); } } } diff --git a/test/ScriptCs.Engine.Roslyn.Tests/Properties/AssemblyInfo.cs b/test/ScriptCs.Engine.Roslyn.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5852ca04..00000000 --- a/test/ScriptCs.Engine.Roslyn.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("ScriptCs.Engine.Roslyn")] -[assembly: AssemblyDescription("")] diff --git a/test/ScriptCs.Engine.Roslyn.Tests/ScriptCs.Engine.Roslyn.Tests.csproj b/test/ScriptCs.Engine.Roslyn.Tests/ScriptCs.Engine.Roslyn.Tests.csproj index 22d4d997..bb8f8ea1 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/ScriptCs.Engine.Roslyn.Tests.csproj +++ b/test/ScriptCs.Engine.Roslyn.Tests/ScriptCs.Engine.Roslyn.Tests.csproj @@ -1,118 +1,29 @@ - - - + - Debug - AnyCPU - {28D11DE5-9F98-4E0A-8CCC-9CDC19110451} - Library - ScriptCs.Tests - ScriptCs.Engine.Roslyn.Tests - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset - 1701 + net461 + win - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - ScriptCsAutoDataAttribute.cs - - - ScriptCsMoqCustomization.cs - - - TestLogger.cs - - - - - - - - - + + + - + + + + + + + + + - - ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - ..\..\packages\AutoFixture.3.18.8\lib\net40\Ploeh.AutoFixture.dll - - - ..\..\packages\AutoFixture.AutoMoq.3.18.8\lib\net40\Ploeh.AutoFixture.AutoMoq.dll - - - ..\..\packages\AutoFixture.Xunit.3.18.8\lib\net40\Ploeh.AutoFixture.Xunit.dll - - - ..\..\packages\Roslyn.Compilers.Common.1.2.20906.2\lib\net45\Roslyn.Compilers.dll - - - ..\..\packages\Roslyn.Compilers.CSharp.1.2.20906.2\lib\net45\Roslyn.Compilers.CSharp.dll - - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - + + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - - - {e79ec231-e27d-4057-91c9-2d001a3a8c3b} - ScriptCs.Engine.Roslyn - + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Engine.Roslyn.Tests/app.config b/test/ScriptCs.Engine.Roslyn.Tests/app.config index 288bbd3e..2aaf612c 100644 --- a/test/ScriptCs.Engine.Roslyn.Tests/app.config +++ b/test/ScriptCs.Engine.Roslyn.Tests/app.config @@ -3,13 +3,17 @@ - - + + - - + + + + + + - \ No newline at end of file + diff --git a/test/ScriptCs.Engine.Roslyn.Tests/packages.config b/test/ScriptCs.Engine.Roslyn.Tests/packages.config deleted file mode 100644 index 3e0f2767..00000000 --- a/test/ScriptCs.Engine.Roslyn.Tests/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Hosting.Tests/ModuleLoaderTests.cs b/test/ScriptCs.Hosting.Tests/ModuleLoaderTests.cs index 40abe9a4..505b8d3d 100644 --- a/test/ScriptCs.Hosting.Tests/ModuleLoaderTests.cs +++ b/test/ScriptCs.Hosting.Tests/ModuleLoaderTests.cs @@ -6,13 +6,13 @@ using System.Reflection; using Moq; using ScriptCs.Contracts; -using ScriptCs.Logging; using Should; using Xunit; -using LogLevel = ScriptCs.Contracts.LogLevel; namespace ScriptCs.Hosting.Tests { + using ScriptCs.Tests; + public class ModuleLoaderTests { public class TheLoadMethod @@ -20,7 +20,7 @@ public class TheLoadMethod private Mock _mockAssemblyResolver = new Mock(); private IList> _modules = new List>(); private Func>> _getModules; - private Mock _mockLogger = new Mock(); + private TestLogProvider _logProvider = new TestLogProvider(); private Mock _mockModule1 = new Mock(); private Mock _mockModule2 = new Mock(); private Mock _mockModule3 = new Mock(); @@ -48,7 +48,7 @@ public TheLoadMethod() [Fact] public void ShouldResolvePathsFromTheAssemblyResolver() { - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (p, c) => { }, c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (p, c) => { }, c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new[] { "c:\test" }, null, null); _mockAssemblyResolver.Verify(r => r.GetAssemblyPaths("c:\test", true)); } @@ -57,7 +57,7 @@ public void ShouldResolvePathsFromTheAssemblyResolver() public void ShouldInvokeTheCatalogActionForEachFile() { var assemblies = new List(); - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => assemblies.Add(a), c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => assemblies.Add(a), c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new[] { "c:\test" }, null, null); assemblies.Count.ShouldEqual(2); } @@ -70,7 +70,7 @@ public void ShouldIgnoreLoadingNativeAssemblies() var mockAssemblies = new List {"managed.dll", "native.dll"}; _mockAssemblyResolver.Setup(a => a.GetAssemblyPaths(It.IsAny(), true)).Returns(mockAssemblies); var assemblies = new List(); - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => assemblies.Add(a), c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => assemblies.Add(a), c => Enumerable.Empty>(), _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new[] { "c:\test" }, null, null); assemblies.Count.ShouldEqual(1); } @@ -78,7 +78,7 @@ public void ShouldIgnoreLoadingNativeAssemblies() [Fact] public void ShouldInitializeModulesThatMatchOnExtension() { - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new string[0], null, "ext1"); _mockModule1.Verify(m => m.Initialize(It.IsAny()), Times.Once()); _mockModule2.Verify(m => m.Initialize(It.IsAny()), Times.Never()); @@ -88,7 +88,7 @@ public void ShouldInitializeModulesThatMatchOnExtension() [Fact] public void ShouldInitializeModulesThatMatchBasedOnName() { - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new string[0], null, null, "module3"); _mockModule1.Verify(m => m.Initialize(It.IsAny()), Times.Never()); _mockModule2.Verify(m => m.Initialize(It.IsAny()), Times.Never()); @@ -98,7 +98,7 @@ public void ShouldInitializeModulesThatMatchBasedOnName() [Fact] public void ShouldInitializeModulesThatAreSetToAutoload() { - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new string[0], null, null); _mockModule4.Verify(m => m.Initialize(It.IsAny()), Times.Once()); } @@ -106,7 +106,7 @@ public void ShouldInitializeModulesThatAreSetToAutoload() [Fact] public void ShouldNotInitializeModulesThatAreNotSetToAutoload() { - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new string[0], null, null); _mockModule1.Verify(m => m.Initialize(It.IsAny()), Times.Never()); } @@ -116,7 +116,7 @@ public void ShouldLoadEngineAssemblyByHandIfItsTheOnlyModule() { var path = Path.Combine("c:\\foo", ModuleLoader.DefaultCSharpModules["roslyn"]); _mockAssemblyUtility.Setup(x => x.LoadFile(path)); - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); loader.Load(null, new string[0], "c:\\foo", ModuleLoader.DefaultCSharpExtension, "roslyn"); _mockAssemblyUtility.Verify(x => x.LoadFile(path), Times.Once()); @@ -126,7 +126,7 @@ public void ShouldLoadEngineAssemblyByHandIfItsTheOnlyModule() public void ShouldLoadEngineModuleFromFile() { _mockAssemblyUtility.Setup(x => x.LoadFile(It.IsAny())).Returns(typeof (DummyModule).Assembly); - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); var config = new ModuleConfiguration(true, string.Empty, false, LogLevel.Debug, true, new Dictionary {{typeof (string), "not loaded"}}); @@ -140,8 +140,8 @@ public void ShouldNotLoadEngineAssemblyByHandIfItsTheOnlyModuleButExtensionIsNot { var path = Path.Combine("c:\\foo", ModuleLoader.DefaultCSharpModules["roslyn"]); _mockAssemblyUtility.Setup(x => x.LoadFile(path)); - var loader = new ModuleLoader(_mockAssemblyResolver.Object, _mockLogger.Object, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); - + var loader = new ModuleLoader(_mockAssemblyResolver.Object, _logProvider, (a, c) => { }, _getModules, _mockFileSystem.Object, _mockAssemblyUtility.Object); + loader.Load(null, new string[0], "c:\\foo", ".fsx", "roslyn"); _mockAssemblyUtility.Verify(x => x.LoadFile(It.IsAny()), Times.Never); } diff --git a/test/ScriptCs.Hosting.Tests/ObjectSerializerTests.cs b/test/ScriptCs.Hosting.Tests/ObjectSerializerTests.cs index e758983f..0be8e25f 100644 --- a/test/ScriptCs.Hosting.Tests/ObjectSerializerTests.cs +++ b/test/ScriptCs.Hosting.Tests/ObjectSerializerTests.cs @@ -1,6 +1,7 @@ using System; using Should; using Xunit; +using System.Diagnostics; namespace ScriptCs.Hosting.Tests { @@ -73,6 +74,17 @@ public void ShouldSerializeDelegates() exception.ShouldBeNull(); } + [Fact] + public void ShouldNotShowExceptionForUnserializableTypes() + { + var obj = new Process(); + + string result = null; + var exception = Record.Exception(() => result = _serializer.Serialize(obj)); + exception.ShouldBeNull(); + Assert.Equal("Couldn't serialize a returned instance of System.Diagnostics.Process", result); + } + [Fact] public void ShouldSerializeWithCircularReferences() { diff --git a/test/ScriptCs.Hosting.Tests/PackageInstallerTests.cs b/test/ScriptCs.Hosting.Tests/PackageInstallerTests.cs index 76634c02..c0947536 100644 --- a/test/ScriptCs.Hosting.Tests/PackageInstallerTests.cs +++ b/test/ScriptCs.Hosting.Tests/PackageInstallerTests.cs @@ -4,7 +4,7 @@ using NuGet; using ScriptCs.Contracts; using ScriptCs.Hosting.Package; -using ScriptCs.Logging; +using ScriptCs.Tests; using Should; using Xunit; @@ -17,14 +17,13 @@ public class InstallPackagesMethod [Fact] public void ShouldThrowArgumentNullExWhenNoPackageIdsPassed() { - var installer = new PackageInstaller(new Mock().Object, new Mock().Object); + var installer = new PackageInstaller(new Mock().Object, new TestLogProvider()); Assert.Throws(() => installer.InstallPackages(null)); } [Fact] public void ShouldInstallAllPassedPackages() { - var logger = new Mock(); var provider = new Mock(); var references = new List @@ -34,7 +33,7 @@ public void ShouldInstallAllPassedPackages() new PackageReference("testId3", VersionUtility.ParseFrameworkName("net40"), new Version("5.0")) }; - var installer = new PackageInstaller(provider.Object, logger.Object); + var installer = new PackageInstaller(provider.Object, new TestLogProvider()); installer.InstallPackages(references); provider.Verify(i => i.InstallPackage(It.IsAny(), It.IsAny()), Times.Exactly(3)); @@ -43,7 +42,6 @@ public void ShouldInstallAllPassedPackages() [Fact] public void ShouldShowErrorIfOneOfPackagesFail() { - var logger = new Mock(); var provider = new Mock(); provider.Setup( i => i.InstallPackage(It.Is(x => x.PackageId == "testId"), It.IsAny())) @@ -56,7 +54,7 @@ public void ShouldShowErrorIfOneOfPackagesFail() new PackageReference("testId3", VersionUtility.ParseFrameworkName("net40"), new Version("5.0")) }; - var installer = new PackageInstaller(provider.Object, logger.Object); + var installer = new PackageInstaller(provider.Object, new TestLogProvider()); var exception = Record.Exception(() => installer.InstallPackages(references, true)); provider.Verify(i => i.InstallPackage(It.IsAny(), It.IsAny()), Times.Exactly(3)); @@ -67,7 +65,6 @@ public void ShouldShowErrorIfOneOfPackagesFail() [Fact] public void ShouldNotInstallExistingPackages() { - var logger = new Mock(); var provider = new Mock(); provider.Setup( i => i.IsInstalled(It.Is(x => x.PackageId == "testId"), It.IsAny())) @@ -80,7 +77,7 @@ public void ShouldNotInstallExistingPackages() new PackageReference("testId3", VersionUtility.ParseFrameworkName("net40"), new Version("5.0")) }; - var installer = new PackageInstaller(provider.Object, logger.Object); + var installer = new PackageInstaller(provider.Object, new TestLogProvider()); installer.InstallPackages(references); provider.Verify(i => i.InstallPackage(It.Is(x => x.PackageId == "testId"), It.IsAny()), Times.Never()); diff --git a/test/ScriptCs.Hosting.Tests/Properties/AssemblyInfo.cs b/test/ScriptCs.Hosting.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index d1a57655..00000000 --- a/test/ScriptCs.Hosting.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("ScriptCs.Hosting.Tests")] -[assembly: AssemblyDescription("")] diff --git a/test/ScriptCs.Hosting.Tests/ReplCommands/OpenVSCommandTests.cs b/test/ScriptCs.Hosting.Tests/ReplCommands/OpenVSCommandTests.cs new file mode 100644 index 00000000..ef8f66f1 --- /dev/null +++ b/test/ScriptCs.Hosting.Tests/ReplCommands/OpenVSCommandTests.cs @@ -0,0 +1,67 @@ +using Moq; +using Moq.Protected; +using ScriptCs.Contracts; +using ScriptCs.Hosting.ReplCommands; +using System; +using System.Collections.Generic; +using Xunit; + +namespace ScriptCs.Hosting.Tests.ReplCommands +{ + public class OpenVsCommandTests + { + public class TheExecuteCommand + { + private Mock _mockCommand; + private OpenVsCommand _command; + private Mock _mockConsole; + private Mock _mockWriter; + private Mock _mockRepl; + + public TheExecuteCommand() + { + _mockConsole = new Mock(); + _mockWriter = new Mock(); + _mockRepl = new Mock(); + _mockRepl.SetupGet(r => r.FileSystem).Returns(new FileSystem()); + _mockCommand = new Mock(_mockConsole.Object, _mockWriter.Object); + _mockCommand.Setup(c => c.PlatformID).Returns(PlatformID.Win32NT); + _mockCommand.Setup(c => c.LaunchSolution(It.IsAny())); + _mockCommand.Protected(); + _command = _mockCommand.Object; + } + + [Fact] + public void OutputsAMessageIfNotWindows8() + { + _mockCommand.Setup(c => c.PlatformID).Returns(PlatformID.MacOSX); + _command.Execute(_mockRepl.Object, new object[] { "test.csx" }); + _mockConsole.Verify(c => c.WriteLine("Requires Windows 8 or later to run")); + } + + [Fact] + public void CreatesTheSolution() + { + _command.Execute(_mockRepl.Object, new object[] { "test.csx" }); + _mockWriter.Verify( + w => + w.WriteSolution(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny>())); + } + + [Fact] + public void LaunchesTheSolution() + { + _command.Execute(_mockRepl.Object, new object[] { "test.csx" }); + _mockCommand.Verify(c => c.LaunchSolution(It.IsAny())); + } + + [Fact] + public void LaunchesTheSolutionEmptyParameter() + { + _command.Execute(_mockRepl.Object, new object[] { }); + _mockCommand.Verify(c => c.LaunchSolution(It.IsAny())); + } + } + } +} diff --git a/test/ScriptCs.Hosting.Tests/RuntimeServicesTests.cs b/test/ScriptCs.Hosting.Tests/RuntimeServicesTests.cs index 2abcee11..b5b888c3 100644 --- a/test/ScriptCs.Hosting.Tests/RuntimeServicesTests.cs +++ b/test/ScriptCs.Hosting.Tests/RuntimeServicesTests.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using Autofac; using Moq; using ScriptCs.Contracts; -using ScriptCs.Logging; using ScriptCs.Tests; using Should; using Xunit; @@ -20,25 +20,25 @@ public class TheContainerProperty private readonly Type _scriptExecutorType; private readonly Type _replType; private readonly Type _scriptEngineType; - private readonly TestLogger _log = new TestLogger(); + private readonly TestLogProvider _logProvider = new TestLogProvider(); private readonly IDictionary _overrides = new Dictionary(); private readonly RuntimeServices _runtimeServices; public TheContainerProperty() { _overrides[typeof(ILineProcessor)] = new List(); - var mockScriptExecutorType = new Mock(); - _scriptExecutorType = mockScriptExecutorType.Object.GetType(); + var mockScriptExecutor = new MockScriptExecutor(); + _scriptExecutorType = mockScriptExecutor.GetType(); var mockReplType = new Mock(); _replType = mockReplType.Object.GetType(); - var mockScriptEngineType = new Mock(); - _scriptEngineType = mockScriptEngineType.Object.GetType(); + var mockScriptEngine = new MockScriptEngine(); + _scriptEngineType = mockScriptEngine.GetType(); - var initializationServices = new InitializationServices(_log, _overrides); + var initializationServices = new InitializationServices(_logProvider, _overrides); _runtimeServices = new RuntimeServices( - _log, + _logProvider, _overrides, _mockConsole.Object, _scriptEngineType, @@ -52,8 +52,8 @@ public TheContainerProperty() [Fact] public void ShouldRegisterTheLoggerInstance() { - var logger = _runtimeServices.Container.Resolve(); - logger.ShouldEqual(_log); + var logProvider = _runtimeServices.Container.Resolve(); + logProvider.ShouldEqual(_logProvider); } [Fact] @@ -73,79 +73,85 @@ public void ShouldRegisterTheExecutor() [Fact] public void ShouldRegisterTheConsoleInstance() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheScriptServices() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultScriptHostFactoryIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultFilePreProcessorIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultScriptPackResolverIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultInstallationProviderIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultPackageInstallerIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultScriptServiceRootIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultFileSystemIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultAssemblyUtilityIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultPackageContainerIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultPackageAssemblyResolverIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] public void ShouldRegisterTheDefaultAssemblyResolverIfNoOverride() { - _runtimeServices.Container.Resolve().ShouldNotBeNull(); + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); + } + + [Fact] + public void ShouldRegisterTheDefaultVisualStudioSolutionWriterIfNoOverride() + { + _runtimeServices.Container.IsRegistered().ShouldBeTrue(); } [Fact] @@ -163,110 +169,114 @@ public void ShouldRegisterTheDefaultLineProcessors() [Fact] public void ShouldRegisterACustomLineProcessor() { - var mock = new Mock(); var processorList = _overrides[typeof(ILineProcessor)] as List; processorList.ShouldNotBeNull(); - processorList.Add(mock.Object.GetType()); + processorList.Add(typeof(MockLineProcessor)); var processors = _runtimeServices.Container.Resolve>(); processors.ShouldNotBeNull(); - processors.Where(p => p.GetType() == mock.Object.GetType()).ShouldNotBeEmpty(); + processors.Where(p => p.GetType() == typeof(MockLineProcessor)).ShouldNotBeEmpty(); } [Fact] public void ShouldRegisterTheOverriddenScriptHostFactory() { - var mock = new Mock(); - _overrides[typeof(IScriptHostFactory)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IScriptHostFactory)] = typeof(MockScriptHostFactory); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockScriptHostFactory)); } [Fact] public void ShouldRegisterTheOverriddenFilePreProcessor() { var mock = new Mock(); - _overrides[typeof(IFilePreProcessor)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IFilePreProcessor)] = typeof(MockFilePreProcessor); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockFilePreProcessor)); } [Fact] public void ShouldRegisterTheOverriddenScriptPackResolver() { - var mock = new Mock(); - _overrides[typeof(IScriptPackResolver)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IScriptPackResolver)] = typeof(MockScriptPackResolver); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockScriptPackResolver)); } [Fact] public void ShouldRegisterTheOverriddenInstallationProvider() { - var mock = new Mock(); - _overrides[typeof(IInstallationProvider)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IInstallationProvider)] = typeof(MockInstallationProvider); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockInstallationProvider)); } [Fact] public void ShouldRegisterTheOverriddenPackageInstaller() { - var mock = new Mock(); - _overrides[typeof(IPackageInstaller)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IPackageInstaller)] = typeof(MockPackageInstaller); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockPackageInstaller)); } [Fact] public void ShouldRegisterTheOverriddenFileSystem() { - var mock = new MockFileSystem(); - _overrides[typeof(IFileSystem)] = mock.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.GetType()); + _overrides[typeof(IFileSystem)] = typeof(MockFileSystem); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockFileSystem)); } [Fact] public void ShouldRegisterTheOverriddenAssemblyUtility() { - var mock = new Mock(); - _overrides[typeof(IAssemblyUtility)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IAssemblyUtility)] = typeof(MockAssemblyUtility); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockAssemblyUtility)); } [Fact] public void ShouldRegisterTheOverriddenConsole() { - var mock = new Mock(); - _overrides[typeof(IConsole)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IConsole)] = typeof(MockConsole); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockConsole)); } [Fact] public void ShouldRegisterTheOverriddenPackageContainer() { - var mock = new Mock(); - _overrides[typeof(IPackageContainer)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IPackageContainer)] = typeof(MockPackageContainer); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockPackageContainer)); } [Fact] public void ShouldRegisterTheOverriddenPackageAssemblyResolver() { - var mock = new Mock(); - _overrides[typeof(IPackageAssemblyResolver)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IPackageAssemblyResolver)] = typeof(MockPackageAssemblyResolver); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockPackageAssemblyResolver)); } [Fact] public void ShouldRegisterTheOverriddenAssemblyResolver() { - var mock = new Mock(); - _overrides[typeof(IAssemblyResolver)] = mock.Object.GetType(); - _runtimeServices.Container.Resolve().ShouldBeType(mock.Object.GetType()); + _overrides[typeof(IAssemblyResolver)] = typeof(MockAssemblyResolver); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockAssemblyResolver)); } [Fact] public void ShouldRegisterTheOverriddenAssemblyResolverInstance() { - var mock = new Mock(); - _overrides[typeof(IAssemblyResolver)] = mock.Object; - _runtimeServices.Container.Resolve().ShouldEqual(mock.Object); + var mock = new MockAssemblyResolver(); + _overrides[typeof(IAssemblyResolver)] = mock; + _runtimeServices.Container.Resolve().ShouldEqual(mock); + } + + [Fact] + public void ShouldRegisterTheOverriddenVisualStudioSolutionWriter() + { + _overrides[typeof(IVisualStudioSolutionWriter)] = typeof(MockVisualStudioSolutionWriter); + _runtimeServices.Container.Resolve().ShouldBeType(typeof(MockVisualStudioSolutionWriter)); + } + + [Fact] + public void ShouldRegisterTheOverriddenRepl() + { + var mock = new Mock(); + _overrides[ typeof( IRepl ) ] = mock.Object.GetType(); + _runtimeServices.Container.Resolve().ShouldBeType( mock.Object.GetType() ); } [Fact] @@ -281,9 +291,9 @@ public void ShouldLogOnDebugAnAssemblyLoadFailure() mockAssemblyUtility.Setup(a => a.IsManagedAssembly(It.IsAny())).Returns(true); _overrides[typeof(IAssemblyUtility)] = mockAssemblyUtility.Object; - var initializationServices = new InitializationServices(_log, _overrides); + var initializationServices = new InitializationServices(_logProvider, _overrides); var runtimeServices = new RuntimeServices( - _log, + _logProvider, _overrides, _mockConsole.Object, _scriptEngineType, @@ -297,7 +307,7 @@ public void ShouldLogOnDebugAnAssemblyLoadFailure() var container = runtimeServices.Container; // assert - _log.Output.ShouldContain( + _logProvider.Output.ShouldContain( "DEBUG: Failure loading assembly: /foo.dll. Exception: Could not load file or assembly 'foo.dll' or one of its dependencies. The system cannot find the file specified."); } @@ -313,9 +323,9 @@ public void ShouldLogAGeneralWarningOnAnAssemblyLoadFailureWhenRunningScript() mockAssemblyUtility.Setup(a => a.IsManagedAssembly(It.IsAny())).Returns(true); _overrides[typeof(IAssemblyUtility)] = mockAssemblyUtility.Object; - var initializationServices = new InitializationServices(_log, _overrides); + var initializationServices = new InitializationServices(_logProvider, _overrides); var runtimeServices = new RuntimeServices( - _log, + _logProvider, _overrides, _mockConsole.Object, _scriptEngineType, @@ -329,7 +339,7 @@ public void ShouldLogAGeneralWarningOnAnAssemblyLoadFailureWhenRunningScript() var container = runtimeServices.Container; // assert - _log.Output.ShouldContain( + _logProvider.Output.ShouldContain( "WARN: Some assemblies failed to load. Launch with '-loglevel debug' to see the details"); } @@ -345,9 +355,9 @@ public void ShouldLogAGeneralWarningOnAnAssemblyLoadFailureWhenRunningInRepl() mockAssemblyUtility.Setup(a => a.IsManagedAssembly(It.IsAny())).Returns(true); _overrides[typeof (IAssemblyUtility)] = mockAssemblyUtility.Object; - var initializationServices = new InitializationServices(_log, _overrides); + var initializationServices = new InitializationServices(_logProvider, _overrides); var runtimeServices = new RuntimeServices( - _log, + _logProvider, _overrides, _mockConsole.Object, _scriptEngineType, @@ -360,7 +370,7 @@ public void ShouldLogAGeneralWarningOnAnAssemblyLoadFailureWhenRunningInRepl() var container = runtimeServices.Container; // assert - _log.Output.ShouldContain( + _logProvider.Output.ShouldContain( "WARN: Some assemblies failed to load. Launch with '-repl -loglevel debug' to see the details"); } @@ -377,9 +387,9 @@ public void ShouldResolveAssembliesBasedOnScriptWorkingDirectory() _overrides[typeof(IFileSystem)] = fsmock.Object; _overrides[typeof(IAssemblyResolver)] = resolvermock.Object; - var initializationServices = new InitializationServices(_log, _overrides); + var initializationServices = new InitializationServices(_logProvider, _overrides); var runtimeServices = new RuntimeServices( - _log, + _logProvider, _overrides, _mockConsole.Object, _scriptEngineType, @@ -396,6 +406,86 @@ public void ShouldResolveAssembliesBasedOnScriptWorkingDirectory() resolvermock.Verify(x => x.GetAssemblyPaths("c:/scripts", false), Times.Exactly(1)); } + private class MockScriptEngine : IScriptEngine + { + public string BaseDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CacheDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string FileName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public ScriptResult Execute(string code, string[] scriptArgs, AssemblyReferences references, IEnumerable namespaces, ScriptPackSession scriptPackSession) + { + throw new NotImplementedException(); + } + } + + private class MockScriptExecutor : IScriptExecutor + { + public AssemblyReferences References => throw new NotImplementedException(); + + public IReadOnlyCollection Namespaces => throw new NotImplementedException(); + + public IScriptEngine ScriptEngine => throw new NotImplementedException(); + + public IFileSystem FileSystem => throw new NotImplementedException(); + + public ScriptPackSession ScriptPackSession => throw new NotImplementedException(); + + public void AddReferences(params Assembly[] references) + { + throw new NotImplementedException(); + } + + public void AddReferences(params string[] references) + { + throw new NotImplementedException(); + } + + public ScriptResult Execute(string script, params string[] scriptArgs) + { + throw new NotImplementedException(); + } + + public ScriptResult ExecuteScript(string script, params string[] scriptArgs) + { + throw new NotImplementedException(); + } + + public void ImportNamespaces(params string[] namespaces) + { + throw new NotImplementedException(); + } + + public void Initialize(IEnumerable paths, IEnumerable scriptPacks, params string[] scriptArgs) + { + throw new NotImplementedException(); + } + + public void RemoveNamespaces(params string[] namespaces) + { + throw new NotImplementedException(); + } + + public void RemoveReferences(params Assembly[] references) + { + throw new NotImplementedException(); + } + + public void RemoveReferences(params string[] references) + { + throw new NotImplementedException(); + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public void Terminate() + { + throw new NotImplementedException(); + } + } + private class MockFileSystem : IFileSystem { public IEnumerable EnumerateFiles( @@ -466,6 +556,8 @@ public string GetFullPath(string path) throw new NotImplementedException(); } + public string TempPath { get; private set; } + public string CurrentDirectory { get { throw new NotImplementedException(); } @@ -567,6 +659,200 @@ public string PackageScriptsFile get { throw new NotImplementedException(); } } } + + private class MockLineProcessor : ILineProcessor + { + public bool ProcessLine(IFileParser parser, FileParserContext context, string line, bool isBeforeCode) + { + throw new NotImplementedException(); + } + } + + private class MockScriptHostFactory : IScriptHostFactory + { + public IScriptHost CreateScriptHost(IScriptPackManager scriptPackManager, string[] scriptArgs) + { + throw new NotImplementedException(); + } + } + + private class MockFilePreProcessor : IFilePreProcessor + { + public void ParseFile(string path, FileParserContext context) + { + throw new NotImplementedException(); + } + + public void ParseScript(List scriptLines, FileParserContext context) + { + throw new NotImplementedException(); + } + + public FilePreProcessorResult ProcessFile(string path) + { + throw new NotImplementedException(); + } + + public FilePreProcessorResult ProcessScript(string script) + { + throw new NotImplementedException(); + } + } + + private class MockScriptPackResolver : IScriptPackResolver + { + public IEnumerable GetPacks() + { + throw new NotImplementedException(); + } + } + + private class MockInstallationProvider : IInstallationProvider + { + public IEnumerable GetRepositorySources(string path) + { + throw new NotImplementedException(); + } + + public void Initialize() + { + throw new NotImplementedException(); + } + + public void InstallPackage(IPackageReference packageId, bool allowPreRelease = false) + { + throw new NotImplementedException(); + } + + public bool IsInstalled(IPackageReference packageId, bool allowPreRelease = false) + { + throw new NotImplementedException(); + } + } + + private class MockPackageInstaller : IPackageInstaller + { + public void InstallPackages(IEnumerable packageIds, bool allowPreRelease = false) + { + throw new NotImplementedException(); + } + } + + private class MockAssemblyUtility : IAssemblyUtility + { + public AssemblyName GetAssemblyName(string path) + { + throw new NotImplementedException(); + } + + public bool IsManagedAssembly(string path) + { + throw new NotImplementedException(); + } + + public Assembly Load(AssemblyName assemblyRef) + { + throw new NotImplementedException(); + } + + public Assembly LoadFile(string path) + { + throw new NotImplementedException(); + } + } + + private class MockConsole : IConsole + { + public ConsoleColor ForegroundColor { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public int Width => throw new NotImplementedException(); + + public void Clear() + { + throw new NotImplementedException(); + } + + public void Exit() + { + throw new NotImplementedException(); + } + + public string ReadLine(string prompt) + { + throw new NotImplementedException(); + } + + public void ResetColor() + { + throw new NotImplementedException(); + } + + public void Write(string value) + { + throw new NotImplementedException(); + } + + public void WriteLine() + { + throw new NotImplementedException(); + } + + public void WriteLine(string value) + { + throw new NotImplementedException(); + } + } + + private class MockPackageContainer : IPackageContainer + { + public void CreatePackageFile() + { + throw new NotImplementedException(); + } + + public IPackageObject FindPackage(string path, IPackageReference packageReference) + { + throw new NotImplementedException(); + } + + public IEnumerable FindReferences(string path) + { + throw new NotImplementedException(); + } + } + + private class MockPackageAssemblyResolver : IPackageAssemblyResolver + { + public IEnumerable GetAssemblyNames(string workingDirectory) + { + throw new NotImplementedException(); + } + + public IEnumerable GetPackages(string workingDirectory) + { + throw new NotImplementedException(); + } + + public void SavePackages() + { + throw new NotImplementedException(); + } + } + + private class MockAssemblyResolver : IAssemblyResolver + { + public IEnumerable GetAssemblyPaths(string path, bool binariesOnly = false) + { + throw new NotImplementedException(); + } + } + private class MockVisualStudioSolutionWriter : IVisualStudioSolutionWriter + { + public string WriteSolution(IFileSystem fs, string script, IVisualStudioSolution solution, IList nestedItems = null) + { + throw new NotImplementedException(); + } + } } } } diff --git a/test/ScriptCs.Hosting.Tests/ScriptCs.Hosting.Tests.csproj b/test/ScriptCs.Hosting.Tests/ScriptCs.Hosting.Tests.csproj index 180459e0..cb5cc998 100644 --- a/test/ScriptCs.Hosting.Tests/ScriptCs.Hosting.Tests.csproj +++ b/test/ScriptCs.Hosting.Tests/ScriptCs.Hosting.Tests.csproj @@ -1,126 +1,30 @@ - - - + - Debug - AnyCPU - {EC03EAA0-94FD-4145-8F78-5F2E30E3FF9A} - Library - Properties - ScriptCs.Hosting.Tests - ScriptCs.Hosting.Tests - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset - 1701 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net461 - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - ScriptCsAutoDataAttribute.cs - - - ScriptCsMoqCustomization.cs - - - TestLogger.cs - - - - - - - - + + + - - + + + + + + + - + + + - - ..\..\packages\Autofac.3.3.1\lib\net40\Autofac.dll - - - ..\..\packages\Microsoft.Web.Xdt.2.1.0\lib\net40\Microsoft.Web.XmlTransform.dll - - - ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - False - ..\..\packages\Nuget.Core.2.8.5\lib\net40-Client\NuGet.Core.dll - - - ..\..\packages\AutoFixture.3.18.8\lib\net40\Ploeh.AutoFixture.dll - - - ..\..\packages\AutoFixture.AutoMoq.3.18.8\lib\net40\Ploeh.AutoFixture.AutoMoq.dll - - - ..\..\packages\AutoFixture.Xunit.3.18.8\lib\net40\Ploeh.AutoFixture.Xunit.dll - - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - - - {9aef2d95-87fb-4829-b384-34bfe076d531} - ScriptCs.Hosting - + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Hosting.Tests/ScriptServicesBuilderTests.cs b/test/ScriptCs.Hosting.Tests/ScriptServicesBuilderTests.cs index 235e1310..ed54c105 100644 --- a/test/ScriptCs.Hosting.Tests/ScriptServicesBuilderTests.cs +++ b/test/ScriptCs.Hosting.Tests/ScriptServicesBuilderTests.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Linq; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Contracts; -using ScriptCs.Logging; using ScriptCs.Tests; using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Hosting.Tests { @@ -24,10 +23,10 @@ public void ShouldResolveScriptServices(ScriptServices scriptServices, [Frozen] } [Theory, ScriptCsAutoData] - public void ShouldLoadScriptPacksIfReplIsTrue(IConsole console, ILog logger, IScriptEngine engine) + public void ShouldLoadScriptPacksIfReplIsTrue(IConsole console, TestLogProvider logProvider) { - var builder = new ScriptServicesBuilder(console, logger); - builder.Overrides[typeof(IScriptEngine)] = engine.GetType(); + var builder = new ScriptServicesBuilder(console, logProvider); + builder.Overrides[typeof(IScriptEngine)] = typeof(MockScriptEngine); builder.Repl(); builder.Build(); var runtimeServices = (RuntimeServices) builder.RuntimeServices; @@ -35,10 +34,10 @@ public void ShouldLoadScriptPacksIfReplIsTrue(IConsole console, ILog logger, ISc } [Theory, ScriptCsAutoData] - public void ShouldLoadScriptPacksIfScriptNameIsSet(IConsole console, ILog logger, IScriptEngine engine) + public void ShouldLoadScriptPacksIfScriptNameIsSet(IConsole console, TestLogProvider logProvider) { - var builder = new ScriptServicesBuilder(console, logger); - builder.Overrides[typeof(IScriptEngine)] = engine.GetType(); + var builder = new ScriptServicesBuilder(console, logProvider); + builder.Overrides[typeof(IScriptEngine)] = typeof(MockScriptEngine); builder.ScriptName(""); builder.Build(); var runtimeServices = (RuntimeServices)builder.RuntimeServices; @@ -46,10 +45,10 @@ public void ShouldLoadScriptPacksIfScriptNameIsSet(IConsole console, ILog logger } [Theory, ScriptCsAutoData] - public void ShoulLoadScriptPacksIfLoadScriptPacksIsTrue(IConsole console, ILog logger, IScriptEngine engine) + public void ShoulLoadScriptPacksIfLoadScriptPacksIsTrue(IConsole console, TestLogProvider logProvider) { - var builder = new ScriptServicesBuilder(console, logger); - builder.Overrides[typeof(IScriptEngine)] = engine.GetType(); + var builder = new ScriptServicesBuilder(console, logProvider); + builder.Overrides[typeof(IScriptEngine)] = typeof(MockScriptEngine); builder.LoadScriptPacks(); builder.Build(); var runtimeServices = (RuntimeServices)builder.RuntimeServices; @@ -57,15 +56,27 @@ public void ShoulLoadScriptPacksIfLoadScriptPacksIsTrue(IConsole console, ILog l } [Theory, ScriptCsAutoData] - public void ShouldNotLoadScriptPacksIfLoadScriptPacksIsFalse(IConsole console, ILog logger, IScriptEngine engine) + public void ShouldNotLoadScriptPacksIfLoadScriptPacksIsFalse(IConsole console, TestLogProvider logProvider) { - var builder = new ScriptServicesBuilder(console, logger); - builder.Overrides[typeof(IScriptEngine)] = engine.GetType(); + var builder = new ScriptServicesBuilder(console, logProvider); + builder.Overrides[typeof(IScriptEngine)] = typeof(MockScriptEngine); builder.LoadScriptPacks(false); builder.Build(); var runtimeServices = (RuntimeServices)builder.RuntimeServices; runtimeServices.InitDirectoryCatalog.ShouldBeFalse(); } + + private class MockScriptEngine : IScriptEngine + { + public string BaseDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CacheDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string FileName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public ScriptResult Execute(string code, string[] scriptArgs, AssemblyReferences references, IEnumerable namespaces, ScriptPackSession scriptPackSession) + { + throw new NotImplementedException(); + } + } } public class TheLoadModulesMethod @@ -111,7 +122,7 @@ public void ShouldLoadTheRoslynModuleWhenTheMonoModuleIsNotSelected([Frozen] Moc } [Theory, ScriptCsAutoData] - public void ShouldFindAllModulesInTheFileSystem([Frozen] Mock typeResolver, [Frozen] Mock moduleLoader, [Frozen] Mock fileSystem, [Frozen] Mock initializationServices, ScriptServicesBuilder builder) + public void ShouldFindAllModulesInTheFileSystem([Frozen] Mock typeResolver, [Frozen] Mock moduleLoader, [Frozen] Mock fileSystem, [Frozen] Mock initializationServices) { typeResolver.Setup(r => r.ResolveType("Mono.Runtime")).Returns((Type)null); fileSystem.SetupGet(fs => fs.GlobalFolder).Returns(@"c:\modules"); diff --git a/test/ScriptCs.Hosting.Tests/VisualStudioSolutionTests.cs b/test/ScriptCs.Hosting.Tests/VisualStudioSolutionTests.cs new file mode 100644 index 00000000..6114bc92 --- /dev/null +++ b/test/ScriptCs.Hosting.Tests/VisualStudioSolutionTests.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Should; +using Xunit; +using System.IO; +using ScriptCs.Contracts; + +namespace ScriptCs.Hosting.Tests +{ + public class VisualStudioSolutionTests + { + public class TheConstructor + { + private VisualStudioSolution _solution = new VisualStudioSolution(); + + [Fact] + public void ShouldInitializeVariables() + { + _solution.Header.ShouldNotBeNull(); + _solution.Projects.ShouldNotBeNull(); + _solution.Global.ShouldNotBeNull(); + } + + [Fact] + public void ShouldAppendTheHeader() + { + var headerBuilder = new StringBuilder(); + headerBuilder.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + headerBuilder.AppendLine("# Visual Studio 2013"); + headerBuilder.AppendLine("VisualStudioVersion = 12.0.30501.0"); + headerBuilder.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); + _solution.Header.ToString().ShouldEqual(headerBuilder.ToString()); + } + } + + public class TheAddGlobalHeaderMethod + { + private Guid _projectGuid = Guid.NewGuid(); + private VisualStudioSolution _builder = new VisualStudioSolution(); + + [Fact] + public void ShouldAppendTheGlobalHeader() + { + _builder.AddGlobalHeader(_projectGuid); + var globalBuilder = new StringBuilder(); + globalBuilder.AppendLine("Global"); + globalBuilder.AppendLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + globalBuilder.AppendLine("\t\tDebug|Any CPU = Debug|Any CPU"); + globalBuilder.AppendLine("\tEndGlobalSection"); + globalBuilder.AppendLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + globalBuilder.AppendFormat("\t\t{{{0}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU{1}", _projectGuid, Environment.NewLine); + globalBuilder.AppendLine("\tEndGlobalSection"); + globalBuilder.AppendLine("\tGlobalSection(SolutionProperties) = preSolution"); + globalBuilder.AppendLine("\t\tHideSolutionNode = FALSE"); + globalBuilder.AppendLine("\tEndGlobalSection"); + globalBuilder.ToString().ShouldEqual(_builder.Global.ToString()); + } + } + + public class TheAddGlobalNestedProjectsMethod + { + [Fact] + public void ShouldAppenedGlobalSectionEntriesForEachProject() + { + var builder = new VisualStudioSolution(); + + var nestedItems = new List(); + var a = Guid.NewGuid(); + var b = Guid.NewGuid(); + var c = Guid.NewGuid(); + + nestedItems.Add(new ProjectItem(a, b)); + nestedItems.Add(new ProjectItem(b, c)); + builder.AddGlobalNestedProjects(nestedItems); + var nestedBuilder = new StringBuilder(); + nestedBuilder.AppendLine("\tGlobalSection(NestedProjects) = preSolution"); + nestedBuilder.AppendFormat("\t\t{{{0}}} = {{{1}}}{2}", nestedItems[0].Project, nestedItems[0].Parent, + Environment.NewLine); + nestedBuilder.AppendFormat("\t\t{{{0}}} = {{{1}}}{2}", nestedItems[1].Project, nestedItems[1].Parent, + Environment.NewLine); + nestedBuilder.AppendLine("\tEndGlobalSection"); + builder.Global.ToString().Contains(nestedBuilder.ToString()); + } + } + + public class TheAddScriptcsProjectMethod + { + private VisualStudioSolution _builder = new VisualStudioSolution(); + private const string _scriptcsPath = "scriptcs.exe"; + private const string _workingPath = "working"; + private const string _args = "test.csx"; + private const bool _attach = true; + private Guid _projectGuid = Guid.NewGuid(); + private string _projects; + + public TheAddScriptcsProjectMethod() + { + _builder.AddScriptcsProject(_scriptcsPath, _workingPath, _args, _attach, _projectGuid); + _projects = _builder.Projects.ToString(); + } + + [Fact] + public void ShouldAppendTheScriptcsProjectSection() + { + var projectBuilder = new StringBuilder(); + + projectBuilder.AppendFormat(@"Project(""{{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}}"") = ""scriptcs"", ""{0}"", ""{{{1}}}""{2}", _scriptcsPath, _projectGuid, Environment.NewLine); + projectBuilder.AppendLine("\tProjectSection(DebuggerProjectSystem) = preProject"); + projectBuilder.AppendLine("\t\tPortSupplier = 00000000-0000-0000-0000-000000000000"); + projectBuilder.AppendFormat("\t\tExecutable = {0}{1}", _scriptcsPath, Environment.NewLine); + projectBuilder.AppendLine("\t\tRemoteMachine = localhost"); + projectBuilder.AppendFormat("\t\tStartingDirectory = {0}{1}", _workingPath, Environment.NewLine); + projectBuilder.AppendFormat("\t\tArguments = {0}{1}", _args, Environment.NewLine); + projectBuilder.AppendLine("\t\tEnvironment = Default"); + projectBuilder.AppendLine("\t\tLaunchingEngine = 00000000-0000-0000-0000-000000000000"); + projectBuilder.AppendLine("\t\tUseLegacyDebugEngines = No"); + projectBuilder.AppendLine("\t\tLaunchSQLEngine = No"); + projectBuilder.AppendFormat("\t\tAttachLaunchAction = {0}{1}", _attach ? "Yes" : "No", Environment.NewLine); + projectBuilder.AppendLine("\tEndProjectSection"); + projectBuilder.AppendLine("EndProject"); + _projects.ShouldEqual(projectBuilder.ToString()); + } + + [Fact] + public void ShouldSetTheProjectElementScriptcsPath() + { + _projects.Contains(string.Format(@"Project(""{{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}}"") = ""scriptcs"", ""{0}""", _scriptcsPath)); + } + + [Fact] + public void ShouldSetTheExecutable() + { + _projects.Contains(string.Format("Executable = {0}", _scriptcsPath)); + } + + [Fact] + public void ShouldSetTheStartingDirectory() + { + _projects.Contains(string.Format("StartingDirectory = {0}", _workingPath)); + } + + [Fact] + public void ShouldSetTheArguements() + { + _projects.Contains(string.Format("Arguments = {0}", _args)); + } + + [Fact] + public void ShouldSetTheAttachAction() + { + _projects.Contains(string.Format("AttachLaunchAction = {0}", _attach ? "Yes" : "No")); + } + } + + public class TheToStringMethod + { + [Fact] + public void BuildsTheSolution() + { + var builder = new VisualStudioSolution(); + builder.Header = new StringBuilder("A"); + builder.Projects = new StringBuilder("B"); + builder.Global = new StringBuilder("C"); + var solution = builder.ToString(); + solution.ShouldEqual("ABC"); + } + } + } +} diff --git a/test/ScriptCs.Hosting.Tests/VisualStudioSolutionWriterTests.cs b/test/ScriptCs.Hosting.Tests/VisualStudioSolutionWriterTests.cs new file mode 100644 index 00000000..47268a3a --- /dev/null +++ b/test/ScriptCs.Hosting.Tests/VisualStudioSolutionWriterTests.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Moq; +using ScriptCs.Contracts; +using Should; +using Xunit; + +namespace ScriptCs.Hosting.Tests +{ + public class VisualStudioSolutionWriterTests + { + public class TheWriteSolutionMethod + { + private readonly Mock _solutionMock; + private readonly Mock _fsMock; + private readonly VisualStudioSolutionWriter _writer; + private readonly IList _nestedItems; + private readonly string _launcher; + + public TheWriteSolutionMethod() + { + _writer = new VisualStudioSolutionWriter(); + _solutionMock = new Mock(); + _fsMock = new Mock(); + _fsMock.SetupGet(fs => fs.PackagesFolder).Returns(@"packages"); + _fsMock.SetupGet(fs => fs.HostBin).Returns("bin"); + _fsMock.SetupGet(fs => fs.CurrentDirectory).Returns("root"); + _fsMock.Setup(fs=>fs.EnumerateFilesAndDirectories(It.IsAny(), It.IsAny(), SearchOption.AllDirectories)).Returns(new[] {Path.Combine("root","file1.csx"), Path.Combine("root", "child1", "file2.csx"), Path.Combine("root", "child1", "child2", "file3.csx")}); + _fsMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false); + _fsMock.SetupGet(fs => fs.TempPath).Returns("temp"); + _nestedItems = new List(); + _launcher = _writer.WriteSolution(_fsMock.Object, "test.csx", _solutionMock.Object, _nestedItems); + } + + [Fact] + public void ShouldAddTheScriptcsProject() + { + var scriptcsPath = Path.Combine("bin", "scriptcs.exe"); + _solutionMock.Verify(fs=>fs.AddScriptcsProject(scriptcsPath, "root", "test.csx -debug -loglevel info", false, It.IsAny())); + } + + [Fact] + public void ShoulGetDirectoryInfo() + { + _writer.Root.Files.ShouldContain("file1.csx"); + var child = _writer.Root.Directories.Values.First(); + child.Files.ShouldContain("file2.csx"); + child = child.Directories.Values.First(); + child.Files.ShouldContain("file3.csx"); + } + + [Fact] + public void ShouldCallAddDirectoryProjectForChild() + { + var child1 = _writer.Root.Directories.Values.First(); + var child2 = child1.Directories.Values.First(); + _nestedItems.Count(i => i.Project == child1.Guid).ShouldEqual(1); + _nestedItems.Count(i => i.Project == child2.Guid).ShouldEqual(1); + } + + [Fact] + public void ShouldAddGlobal() + { + _solutionMock.Verify(s=>s.AddGlobal(It.IsAny(), _nestedItems)); + } + + [Fact] + public void ShouldWriteTheSolution() + { + _fsMock.Verify(fs=>fs.WriteToFile(It.IsAny(), It.IsAny())); + } + + [Fact] + public void ShouldReturnALauncherInTheTempFolder() + { + _launcher.ShouldNotBeNull(); + _launcher.ShouldStartWith("temp"); + } + } + } +} diff --git a/test/ScriptCs.Hosting.Tests/app.config b/test/ScriptCs.Hosting.Tests/app.config index 288bbd3e..fe865d69 100644 --- a/test/ScriptCs.Hosting.Tests/app.config +++ b/test/ScriptCs.Hosting.Tests/app.config @@ -2,14 +2,26 @@ - - - - + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/test/ScriptCs.Hosting.Tests/packages.config b/test/ScriptCs.Hosting.Tests/packages.config deleted file mode 100644 index 9738e7e0..00000000 --- a/test/ScriptCs.Hosting.Tests/packages.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Tests.Acceptance/CommandLine.cs b/test/ScriptCs.Tests.Acceptance/CommandLine.cs index 854cdd53..b8e78bef 100644 --- a/test/ScriptCs.Tests.Acceptance/CommandLine.cs +++ b/test/ScriptCs.Tests.Acceptance/CommandLine.cs @@ -15,7 +15,7 @@ public static void UnexpectedOption(Exception exception) var scenario = MethodBase.GetCurrentMethod().GetFullName(); "When I execute scriptcs with an unknown option" - .f(() => exception = Record.Exception(() => ScriptCsExe.Run( + .x(() => exception = Record.Exception(() => ScriptCsExe.Run( new[] { "-unknownoption" @@ -23,16 +23,14 @@ public static void UnexpectedOption(Exception exception) ScenarioDirectory.Create(scenario)))); "Then scriptcs errors" - .f(() => exception.ShouldBeType()); + .x(() => exception.ShouldBeType()); "And I see an error message regarding the unknown option" - .f(() => + .x(() => { - exception.Message.ShouldContain("unknownoption"); + Console.WriteLine(exception); + exception.Message.ShouldContain("Usage: scriptcs options"); }); - - "And I see scriptcs usage details" - .f(() => exception.Message.ShouldContain("Usage:")); } } } diff --git a/test/ScriptCs.Tests.Acceptance/Configuration.cs b/test/ScriptCs.Tests.Acceptance/Configuration.cs index 8e07199f..2c0861ed 100644 --- a/test/ScriptCs.Tests.Acceptance/Configuration.cs +++ b/test/ScriptCs.Tests.Acceptance/Configuration.cs @@ -13,17 +13,17 @@ public static void LocalConfiguration(ScenarioDirectory directory, string output var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a hello world script" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(""Hello world!"");")); "And a local config file specfying the log level as debug" - .f(() => directory.WriteLine("scriptcs.opts", @"{ logLevel: ""debug"" }")); + .x(() => directory.WriteLine("scriptcs.opts", @"{ logLevel: ""debug"" }")); "When I execute the script without the log level option" - .f(() => output = ScriptCsExe.Run("foo.csx", false, directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", false, directory)); "Then I see debug messages" - .f(() => output.ShouldContain("DEBUG:")); + .x(() => output.ShouldContain("DEBUG:")); } [Scenario] @@ -32,21 +32,21 @@ public static void CustomConfiguration(ScenarioDirectory directory, string outpu var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a hello world script" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(""Hello world!"");")); "And a local config file specfying to run as debug" - .f(() => directory.WriteLine("custom.opts", @"{ logLevel: ""debug"" }")); + .x(() => directory.WriteLine("custom.opts", @"{ logLevel: ""debug"" }")); "When I execute the script without the log level option but specifying the custom config" - .f(() => + .x(() => { - var args = new[] { "-config", "custom.opts", }; + var args = new[] { "--config", "custom.opts", }; output = ScriptCsExe.Run("foo.csx", false, args, directory); }); "Then I see debug messages" - .f(() => output.ShouldContain("DEBUG:")); + .x(() => output.ShouldContain("DEBUG:")); } } } diff --git a/test/ScriptCs.Tests.Acceptance/DirectoryCleaning.cs b/test/ScriptCs.Tests.Acceptance/DirectoryCleaning.cs index ca6b63b7..c7cc8e5e 100644 --- a/test/ScriptCs.Tests.Acceptance/DirectoryCleaning.cs +++ b/test/ScriptCs.Tests.Acceptance/DirectoryCleaning.cs @@ -14,22 +14,22 @@ public static void CleaningADirectory(ScenarioDirectory directory, string output var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a directory" - .f(() => directory = ScenarioDirectory.Create(scenario)); + .x(() => directory = ScenarioDirectory.Create(scenario)); "And the directory has an installed package" - .f(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory)); + .x(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory)); "And the directory has an assembly cache" - .f(() => directory.WriteLine(Path.Combine(directory.Map(ScriptCsExe.DllCacheFolder), "foo.txt"), null)); + .x(() => directory.WriteLine(Path.Combine(directory.Map(ScriptCsExe.DllCacheFolder), "foo.txt"), null)); "When I clean the directory" - .f(() => ScriptCsExe.Clean(directory)); + .x(() => ScriptCsExe.Clean(directory)); "Then the packages folder is removed" - .f(() => Directory.Exists(directory.Map(ScriptCsExe.PackagesFolder)).ShouldBeFalse()); + .x(() => Directory.Exists(directory.Map(ScriptCsExe.PackagesFolder)).ShouldBeFalse()); "And the assembly cache folder is removed" - .f(() => Directory.Exists(directory.Map(ScriptCsExe.DllCacheFolder)).ShouldBeFalse()); + .x(() => Directory.Exists(directory.Map(ScriptCsExe.DllCacheFolder)).ShouldBeFalse()); } } } diff --git a/test/ScriptCs.Tests.Acceptance/ImplicitInstallation.cs b/test/ScriptCs.Tests.Acceptance/ImplicitInstallation.cs index a2d6a8f3..9b2edf33 100644 --- a/test/ScriptCs.Tests.Acceptance/ImplicitInstallation.cs +++ b/test/ScriptCs.Tests.Acceptance/ImplicitInstallation.cs @@ -9,16 +9,16 @@ public static class ImplicitInstallation { [Scenario] - public static void LegacyFileSystem(ScenarioDirectory directory, string output) + public static void Execute(ScenarioDirectory directory, string output) { var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Adder to print the sum of 1234 and 5678" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(Require().Add(1234, 5678));")); - "And a legacy packages file declaring the ScriptCs.Adder dependency" - .f(() => + "And a packages file declaring the ScriptCs.Adder dependency" + .x(() => { var nugetConfig = @" @@ -39,15 +39,15 @@ public static void LegacyFileSystem(ScenarioDirectory directory, string output) "; - directory.WriteLine("nuget.config", nugetConfig); - directory.WriteLine("packages.config", packagesConfig); + directory.WriteLine("scriptcs_nuget.config", nugetConfig); + directory.WriteLine("scriptcs_packages.config", packagesConfig); }); "When execute the script" - .f(() => output = ScriptCsExe.Run("foo.csx", directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); "Then I see 6912" - .f(() => output.ShouldContain("6912")); + .x(() => output.ShouldContain("6912")); } } } diff --git a/test/ScriptCs.Tests.Acceptance/LooseScriptExecution.cs b/test/ScriptCs.Tests.Acceptance/LooseScriptExecution.cs new file mode 100644 index 00000000..71af4017 --- /dev/null +++ b/test/ScriptCs.Tests.Acceptance/LooseScriptExecution.cs @@ -0,0 +1,96 @@ +namespace ScriptCs.Tests.Acceptance +{ + using System; + using System.Reflection; + using ScriptCs.Tests.Acceptance.Support; + using Should; + using Xbehave; + using Xunit; + + public static class LooseScriptExecution + { + [Scenario] + [Example(true)] + [Example(false)] + public static void HelloWorld(bool debug, ScenarioDirectory directory, string output, string[] args, string script) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a hello world script" + .x(() => + { + directory = ScenarioDirectory.Create(scenario); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + script = @"Console.WriteLine(""""""Hello World!"""""");"; + } + else + { + script = @"'Console.WriteLine(""Hello World!"");'"; + } + args = new[] {"-e", script}; + }); + + "When I execute the script with debug set to {0}" + .x(() => output = ScriptCsExe.Run(args, debug, directory)); + + "Then I see 'Hello World!'" + .x(() => output.ShouldContain("Hello World!")); + } + + [Scenario] + [Example(true)] + [Example(false)] + public static void ScriptThrowsAnException(bool debug, ScenarioDirectory directory, Exception exception, string[] args, string script) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which throws an exception" + .x(() => + { + directory = ScenarioDirectory.Create(scenario); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + script = @"""throw new Exception(""""""BOOM!"""""");"""; + } + else + { + script = @"'throw new Exception(""BOOM!"");'"; + } + + args = new[] {"-e", script}; + }); + + "When I execute the script with debug set to {0}" + .x(() => exception = Record.Exception(() => ScriptCsExe.Run(args, debug, directory))); + + "Then scriptcs fails" + .x(() => exception.ShouldBeType()); + + "And I see the exception message" + .x(() => + { + exception.Message.ShouldContain("BOOM!"); + }); + } + + [Scenario] + public static void ScriptCanAccessEnv(ScenarioDirectory directory, string output, string[] args, string script) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which access Env" + .x(() => + { + directory = ScenarioDirectory.Create(scenario); + script = "Console.WriteLine(Env)"; + args = new[] {"-e", script}; + }); + "When I execute the script" + .x(() => output = ScriptCsExe.Run(args, directory)); + + "Then the Env object is displayed" + .x(() => output.ShouldContain("ScriptCs.ScriptEnvironment")); + } + } +} diff --git a/test/ScriptCs.Tests.Acceptance/Migration.cs b/test/ScriptCs.Tests.Acceptance/Migration.cs deleted file mode 100644 index dcf0ea4d..00000000 --- a/test/ScriptCs.Tests.Acceptance/Migration.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace ScriptCs.Tests.Acceptance -{ - using System.IO; - using System.Reflection; - using ScriptCs.Tests.Acceptance.Support; - using Should; - using Xbehave; - - public static class Migration - { - [Scenario] - public static void Migrating(ScenarioDirectory directory, string output) - { - var scenario = MethodBase.GetCurrentMethod().GetFullName(); - - "Given a script directory with a full population of legacy artifacts including a hello world script" - .f(() => directory = ScenarioDirectory.Create(scenario) - .WriteLine("bin/foo.txt", null) - .WriteLine(".cache/foo.txt", null) - .WriteLine("packages/foo.txt", null) - .WriteLine("packages.config", @"") - .WriteLine("nuget.config", @"") - .WriteLine("hello.csx", @"Console.WriteLine(""Hello, World!"");")); - - "When I execute the script" - .f(() => ScriptCsExe.Run("hello.csx", directory)); - - "Then the artifacts are migrated" - .f(() => - { - File.Exists(directory.Map("bin/foo.txt")).ShouldBeTrue("bin/ is unchanged"); - File.Exists(directory.Map(".cache/foo.txt")).ShouldBeFalse(".cache/ is renamed to .scriptcs_cache/"); - File.Exists(directory.Map("packages/foo.txt")).ShouldBeTrue("packages/ is unchanged"); - File.Exists(directory.Map("packages.config")).ShouldBeTrue("packages.config is unchanged"); - File.Exists(directory.Map("nuget.config")).ShouldBeTrue("nuget.config is unchanged"); - - File.Exists(directory.Map("scriptcs_bin/foo.txt")).ShouldBeTrue("bin/ is copied to scriptcs_bin/"); - File.Exists(directory.Map(".scriptcs_cache/foo.txt")).ShouldBeTrue(".scriptcs_cache/ is renamed from .cache/"); - File.Exists(directory.Map("scriptcs_packages/foo.txt")).ShouldBeTrue("packages/ is copied to scriptcs_packages/"); - File.Exists(directory.Map("scriptcs_packages.config")).ShouldBeTrue("packages.config is copied to scriptcs_packages.config"); - File.Exists(directory.Map("scriptcs_nuget.config")).ShouldBeTrue("nuget.config is copied to scriptcs_nuget.config"); - }); - } - } -} diff --git a/test/ScriptCs.Tests.Acceptance/PackageInstallation.cs b/test/ScriptCs.Tests.Acceptance/PackageInstallation.cs index 0c81c3fa..f04b3e20 100644 --- a/test/ScriptCs.Tests.Acceptance/PackageInstallation.cs +++ b/test/ScriptCs.Tests.Acceptance/PackageInstallation.cs @@ -9,29 +9,33 @@ public static class PackageInstallation { [Scenario] - public static void InstallingAPackage(ScenarioDirectory directory, string output) + public static void InstallingAPackage(ScenarioDirectory directory) { var scenario = MethodBase.GetCurrentMethod().GetFullName(); "When I install ScriptCs.Adder" - .f(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory = ScenarioDirectory.Create(scenario))); + .x(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory = ScenarioDirectory.Create(scenario))); "Then the ScriptCs.Adder NuGet package is added to the packages folder" - .f(() => File.Exists( + .x(() => File.Exists( Path.Combine( directory.Map(ScriptCsExe.PackagesFolder), "ScriptCs.Adder.Local.0.1.1/ScriptCs.Adder.Local.0.1.1.nupkg")) .ShouldBeTrue()); "And the ScriptCs.Adder assembly is extracted" - .f(() => File.Exists( - Path.Combine( - directory.Map(ScriptCsExe.PackagesFolder), - "ScriptCs.Adder.Local.0.1.1/lib/net45/ScriptCs.Adder.dll")) - .ShouldBeTrue()); + .x(() => + + + File.Exists( + Path.Combine( + directory.Map(ScriptCsExe.PackagesFolder), + "ScriptCs.Adder.Local.0.1.1/lib/net45/ScriptCs.Adder.dll")) + .ShouldBeTrue() + ); "And ScriptCs.Adder is added to the packages file" - .f(() => File.ReadAllText(directory.Map(ScriptCsExe.PackagesFile)).ShouldContain( + .x(() => File.ReadAllText(directory.Map(ScriptCsExe.PackagesFile)).ShouldContain( @"")); } } diff --git a/test/ScriptCs.Tests.Acceptance/PackageSaving.cs b/test/ScriptCs.Tests.Acceptance/PackageSaving.cs index 8cf94291..dbc11804 100644 --- a/test/ScriptCs.Tests.Acceptance/PackageSaving.cs +++ b/test/ScriptCs.Tests.Acceptance/PackageSaving.cs @@ -14,17 +14,17 @@ public static void SavingAPackage(ScenarioDirectory directory, string output) var scenario = MethodBase.GetCurrentMethod().GetFullName(); "When I install ScriptCs.Adder manually" - .f(() => + .x(() => { ScriptCsExe.Install("ScriptCs.Adder.Local", directory = ScenarioDirectory.Create(scenario)); directory.DeleteFile(ScriptCsExe.PackagesFile); }); "And I save packages" - .f(() => ScriptCsExe.Save(directory)); + .x(() => ScriptCsExe.Save(directory)); "Then ScriptCs.Adder is added to the packages file" - .f(() => File.ReadAllText(directory.Map(ScriptCsExe.PackagesFile)).ShouldContain( + .x(() => File.ReadAllText(directory.Map(ScriptCsExe.PackagesFile)).ShouldContain( @"")); } } diff --git a/test/ScriptCs.Tests.Acceptance/Packages.cs b/test/ScriptCs.Tests.Acceptance/Packages.cs index a7fa3820..7527fc5c 100644 --- a/test/ScriptCs.Tests.Acceptance/Packages.cs +++ b/test/ScriptCs.Tests.Acceptance/Packages.cs @@ -9,23 +9,23 @@ public static class Packages { - [Scenario] + [Scenario(Skip = "Failing with a path length exception")] public static void PackageContainsAFrameworkAssemblyReference(ScenarioDirectory directory, Exception exception) { var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a simple script" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", "Console.WriteLine();")); "When I install a package which contains a framework assembly reference" - .f(() => ScriptCsExe.Install("FrameworkAssemblyReferencer", directory)); + .x(() => ScriptCsExe.Install("FrameworkAssemblyReferencer", directory)); "And I execute the script" - .f(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", directory))); + .x(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", directory))); "Then there should be no errors" - .f(() => exception.ShouldBeNull()); + .x(() => exception.ShouldBeNull()); } [Scenario] @@ -34,20 +34,20 @@ public static void PackagesWithDuplicateAssemblies(ScenarioDirectory directory, var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a simple script" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", "Console.WriteLine();")); "When I install a package" - .f(() => ScriptCsExe.Install("Duplicate.A", directory)); + .x(() => ScriptCsExe.Install("Duplicate.A", directory)); "And I install another package containing the same assembly" - .f(() => ScriptCsExe.Install("Duplicate.B", directory)); + .x(() => ScriptCsExe.Install("Duplicate.B", directory)); "And I execute the script" - .f(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", directory))); + .x(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", directory))); "Then there should be no errors" - .f(() => exception.ShouldBeNull()); + .x(() => exception.ShouldBeNull()); } } } diff --git a/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj b/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj index 027a25ff..765fdba3 100644 --- a/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj +++ b/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj @@ -1,112 +1,43 @@ - - - + - Debug - AnyCPU - {10684649-2922-41F5-AB9B-20B127CBF92C} - Library - ScriptCs.Tests.Acceptance - ScriptCs.Tests.Acceptance - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset + net461 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - 1701 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - + - - Support\Guard.cs - - - - - - - - - - - - - - - - - - + + + + - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\Xbehave.1.1.0\lib\net45\Xbehave.dll - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - + - - + + Always + + Always - + Always - + Always - + Always - + Always - + Always - + Always - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj.DotSettings b/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj.DotSettings new file mode 100644 index 00000000..73e96563 --- /dev/null +++ b/test/ScriptCs.Tests.Acceptance/ScriptCs.Tests.Acceptance.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/test/ScriptCs.Tests.Acceptance/ScriptExecution.cs b/test/ScriptCs.Tests.Acceptance/ScriptExecution.cs index 769bb57a..aa3af367 100644 --- a/test/ScriptCs.Tests.Acceptance/ScriptExecution.cs +++ b/test/ScriptCs.Tests.Acceptance/ScriptExecution.cs @@ -1,4 +1,6 @@ -namespace ScriptCs.Tests.Acceptance +using System.IO; + +namespace ScriptCs.Tests.Acceptance { using System; using System.Reflection; @@ -17,14 +19,14 @@ public static void HelloWorld(bool debug, ScenarioDirectory directory, string ou var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a hello world script" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(""Hello world!"");")); "When I execute the script with debug set to {0}" - .f(() => output = ScriptCsExe.Run("foo.csx", debug, directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", debug, directory)); "Then I see 'Hello world!'" - .f(() => output.ShouldContain("Hello world!")); + .x(() => output.ShouldContain("Hello world!")); } [Scenario] @@ -35,17 +37,49 @@ public static void ScriptThrowsAnException(bool debug, ScenarioDirectory directo var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which throws an exception" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"throw new Exception(""BOOM!"");")); "When I execute the script with debug set to {0}" - .f(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", debug, directory))); + .x(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", debug, directory))); "Then scriptcs fails" - .f(() => exception.ShouldBeType()); + .x(() => exception.ShouldBeType()); "And I see the exception message" - .f(() => exception.Message.ShouldContain("BOOM!")); + .x(() => exception.Message.ShouldContain("BOOM!")); + } + + [Scenario] + public static void ScriptCanWorkWithUsingStatic(ScenarioDirectory directory, string output) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which defined a static import" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", "using static System.Console;" + Environment.NewLine + @"WriteLine(""Hello world!"");")); + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then I see 'Hello world!'" + .x(() => output.ShouldContain("Hello world!")); + } + + [Scenario] + public static void ScriptingEngineShouldSupportCSharp71(ScenarioDirectory directory, string output) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which uses C# 7.1 language feature - named tuples" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", @"var x = 1; var y = 2; var tuple = (x,y); Console.WriteLine(""Sum="" + (tuple.x + tuple.y))")); + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then I see the output from the named tuples." + .x(() => output.ShouldContain("Sum=3")); } [Scenario] @@ -53,15 +87,91 @@ public static void ScriptCanAccessEnv(ScenarioDirectory directory, string output { var scenario = MethodBase.GetCurrentMethod().GetFullName(); - "Given a script which access Env" - .f(() => directory = ScenarioDirectory.Create(scenario) + "Given a script which accesses Env" + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", "Console.WriteLine(Env)")); "When I execute the script" - .f(()=> output = ScriptCsExe.Run("foo.csx", directory)); + .x(()=> output = ScriptCsExe.Run("foo.csx", directory)); "Then the Env object is displayed" - .f(() => output.ShouldContain("ScriptCs.ScriptEnvironment")); + .x(() => output.ShouldContain("ScriptCs.ScriptEnvironment")); } + + [Scenario] + public static void ScriptCanUseDynamic(ScenarioDirectory directory, string output ) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which uses dynamic" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", @"dynamic obj = new ExpandoObject(); obj.foo = ""bar""; Console.WriteLine(obj.foo); ;")); + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then the dynamic value is properly returned " + .x(() => output.ShouldContain("bar")); + + } + + [Scenario] + public static void ScriptAssemblyIsSet(ScenarioDirectory directory, string output) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which accesses Env.ScriptAssembly" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", "Console.WriteLine(Env.ScriptAssembly)")); + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then the Assembly is displayed" + .x(() => output.ShouldContain("Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")); + + } + + [Scenario] + public static void ScriptPathIsSet(ScenarioDirectory directory, string output) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which accesses Env.ScriptPath" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", "Console.WriteLine(Env.ScriptPath)")); + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then the ScriptPath is displayed" + .x(() => output.ShouldContain("foo.csx")); + } + + [Scenario] + public static void LoadedScriptsIsSet(ScenarioDirectory directory, string output) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which loads another script and accesses Env.LoadedScripts" + .x(() => + { + directory = ScenarioDirectory.Create(scenario) + .WriteLine( + "foo.csx", "#load bar.csx;" + Environment.NewLine + + "Console.WriteLine(Env.LoadedScripts.First());" + ); + directory.WriteLine("bar.csx", ""); + }); + + + "When I execute the script" + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); + + "Then the loaded script path is displayed" + .x(() => output.ShouldContain("bar.csx")); + } + + } } diff --git a/test/ScriptCs.Tests.Acceptance/ScriptLibraries.cs b/test/ScriptCs.Tests.Acceptance/ScriptLibraries.cs index 5bac9332..1049173e 100644 --- a/test/ScriptCs.Tests.Acceptance/ScriptLibraries.cs +++ b/test/ScriptCs.Tests.Acceptance/ScriptLibraries.cs @@ -16,43 +16,43 @@ public static void UsingAMethodInAScriptLibrary(ScenarioDirectory directory, str var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Calculator to print the sum of 40 and 2" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(new Calculator().Add(40, 2));")); "And ScriptCs.Calculator is installed" - .f(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); + .x(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); "When executing the script" - .f(() => + .x(() => { var scriptArgs = new[] { "-loglevel", "info" }; output = ScriptCsExe.Run("foo.csx", false, Enumerable.Empty(), scriptArgs, directory); }); "Then I see 42" - .f(() => output.ShouldContain("42")); + .x(() => output.ShouldContain("42")); "Then I see INFO outputted from the required Logger script pack" - .f(() => output.ShouldContain("INFO")); + .x(() => output.ShouldContain("INFO")); } - [Scenario] + [Scenario(Skip = "Failing with a path length exception")] public static void UsingAMethodInAScriptLibraryInTheRepl(ScenarioDirectory directory, string output) { var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Calculator" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(""Type:"" + new Calculator().GetType().Name);" + Environment.NewLine + "Environment.Exit(0);")); "And ScriptCs.Calculator is installed" - .f(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); + .x(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); "When executing the script into REPL" - .f(() => output = ScriptCsExe.Run("foo.csx", false, new[] { "-r" }, directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", false, new[] { "-r" }, directory)); "Then the ScriptCs.Calculator instance is created" - .f(() => output.ShouldContain("Type:Calculator")); + .x(() => output.ShouldContain("Type:Calculator")); } [Scenario] @@ -61,7 +61,7 @@ public static void LoadingFromANonRootedScript(ScenarioDirectory directory, Scen var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Calculator and it is non-rooted" - .f(() => + .x(() => { directory = ScenarioDirectory.Create(scenario); scriptDirectory = ScenarioDirectory.Create(Path.Combine(scenario, "script")) @@ -69,13 +69,13 @@ public static void LoadingFromANonRootedScript(ScenarioDirectory directory, Scen }); "And ScriptCs.Calculator is installed" - .f(() => ScriptCsExe.Install("ScriptCs.Calculator", scriptDirectory)); + .x(() => ScriptCsExe.Install("ScriptCs.Calculator", scriptDirectory)); "When executing the script" - .f(() => output = ScriptCsExe.Run(Path.Combine("script", "foo.csx"), directory)); + .x(() => output = ScriptCsExe.Run(Path.Combine("script", "foo.csx"), false, directory)); "Then the ScriptCs.Calculator instance is created" - .f(() => output.ShouldContain("Type:Calculator")); + .x(() => output.ShouldContain("Type:Calculator")); } @@ -85,17 +85,17 @@ public static void UsingALoadedMethodInAScriptLibrary(ScenarioDirectory director var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Calculator to print the product of 7 and 6" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(new Calculator().Multiply(7, 6));")); "And ScriptCs.Calculator is installed" - .f(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); + .x(() => ScriptCsExe.Install("ScriptCs.Calculator", directory)); "When executing the script" - .f(() => output = ScriptCsExe.Run("foo.csx", directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); "Then I see 42" - .f(() => output.ShouldContain("42")); + .x(() => output.ShouldContain("42")); } } } diff --git a/test/ScriptCs.Tests.Acceptance/ScriptPacks.cs b/test/ScriptCs.Tests.Acceptance/ScriptPacks.cs index 76a3a9cd..d5f16da3 100644 --- a/test/ScriptCs.Tests.Acceptance/ScriptPacks.cs +++ b/test/ScriptCs.Tests.Acceptance/ScriptPacks.cs @@ -4,6 +4,9 @@ using ScriptCs.Tests.Acceptance.Support; using Should; using Xbehave; + using System; + using Should.Core.Assertions; + using ScriptCs.Exceptions; public static class ScriptPacks { @@ -13,17 +16,41 @@ public static void UsingAScriptPack(ScenarioDirectory directory, string output) var scenario = MethodBase.GetCurrentMethod().GetFullName(); "Given a script which uses ScriptCs.Adder to print the sum of 1234 and 5678" - .f(() => directory = ScenarioDirectory.Create(scenario) + .x(() => directory = ScenarioDirectory.Create(scenario) .WriteLine("foo.csx", @"Console.WriteLine(Require().Add(1234, 5678));")); "And ScriptCs.Adder is installed" - .f(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory)); + .x(() => ScriptCsExe.Install("ScriptCs.Adder.Local", directory)); "When execute the script" - .f(() => output = ScriptCsExe.Run("foo.csx", directory)); + .x(() => output = ScriptCsExe.Run("foo.csx", directory)); "Then I see 6912" - .f(() => output.ShouldContain("6912")); + .x(() => output.ShouldContain("6912")); + } + + [Scenario] + public static void UsingAnUnavailableScriptPack(ScenarioDirectory directory, string output, Exception exception) + { + var scenario = MethodBase.GetCurrentMethod().GetFullName(); + + "Given a script which uses a non-existing script pack 'Foo'" + .x(() => directory = ScenarioDirectory.Create(scenario) + .WriteLine("foo.csx", @"var fooContext = Require(); + class Foo : ScriptCs.Contracts.IScriptPackContext{} + Console.WriteLine(""hi"");")); + + "When I execute the script" + .x(() => exception = Record.Exception(() => ScriptCsExe.Run("foo.csx", directory))); + + "Then scriptcs fails" + .x(() => exception.ShouldBeType()); + + "With a script pack exception" + .x(() => + { + exception.Message.ShouldContain("Tried to resolve a script pack 'Submission#0+Foo', but such script pack is not available in the current execution context."); + }); } } } diff --git a/test/ScriptCs.Tests.Acceptance/Support/FileSystem.cs b/test/ScriptCs.Tests.Acceptance/Support/FileSystem.cs index b73b6a90..d21f4580 100644 --- a/test/ScriptCs.Tests.Acceptance/Support/FileSystem.cs +++ b/test/ScriptCs.Tests.Acceptance/Support/FileSystem.cs @@ -1,4 +1,6 @@ -namespace ScriptCs.Tests.Acceptance.Support +using System.Diagnostics; + +namespace ScriptCs.Tests.Acceptance.Support { using System; using System.Globalization; @@ -10,6 +12,8 @@ public static class FileSystem { public static void EnsureDirectoryCreated(string path) { + Debug.WriteLine($"Current Directory [{Environment.CurrentDirectory.Length}]: {Environment.CurrentDirectory}"); + if (Directory.Exists(path)) { return; diff --git a/test/ScriptCs.Tests.Acceptance/Support/MethodBaseExtensions.cs b/test/ScriptCs.Tests.Acceptance/Support/MethodBaseExtensions.cs index fc864799..0acc3ce1 100644 --- a/test/ScriptCs.Tests.Acceptance/Support/MethodBaseExtensions.cs +++ b/test/ScriptCs.Tests.Acceptance/Support/MethodBaseExtensions.cs @@ -1,4 +1,7 @@ -namespace ScriptCs.Tests.Acceptance.Support +using System.Security.Cryptography; +using System.Text; + +namespace ScriptCs.Tests.Acceptance.Support { using System.Reflection; using ScriptCs; @@ -7,11 +10,34 @@ public static class MethodBaseExtensions { public static string GetFullName(this MethodBase method) { - Guard.AgainstNullArgument("method", method); + Guard.AgainstNullArgument(nameof(method), method); return method.DeclaringType == null ? method.Name - : string.Concat(method.DeclaringType.FullName, ".", method.Name); + : string.Concat(GenerateSimpleHash(method.DeclaringType.FullName), ".", method.Name); + } + + /// + /// Windows cannot handle long path, so create a short path + /// + /// + /// + private static string GenerateSimpleHash(string name) + { + // use a hash instead of random characters + // so we have some consistency in debugging folders + using (var md5Hash = MD5.Create()) + { + var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(name)); + var hash = new StringBuilder(10); + + for (var i = 0; i < 5; i++) + { + hash.Append(data[i].ToString("x2")); + } + + return hash.ToString(); + } } } } diff --git a/test/ScriptCs.Tests.Acceptance/Support/ScenarioDirectory.cs b/test/ScriptCs.Tests.Acceptance/Support/ScenarioDirectory.cs index a9bfc2a9..d102e21e 100644 --- a/test/ScriptCs.Tests.Acceptance/Support/ScenarioDirectory.cs +++ b/test/ScriptCs.Tests.Acceptance/Support/ScenarioDirectory.cs @@ -1,5 +1,8 @@ -namespace ScriptCs.Tests.Acceptance.Support +using System; + +namespace ScriptCs.Tests.Acceptance.Support { + using System.Diagnostics; using System.IO; public sealed class ScenarioDirectory @@ -11,6 +14,8 @@ public sealed class ScenarioDirectory public static ScenarioDirectory Create(string scenario) { var name = Path.Combine(rootDirectory, scenario); + if (name == null) throw new Exception("Invalid directory"); + Debug.WriteLine($"Scenarios Dir [{name.Length}]: {name}"); FileSystem.EnsureDirectoryDeleted(name); FileSystem.EnsureDirectoryCreated(name); return new ScenarioDirectory(name); @@ -34,6 +39,7 @@ public ScenarioDirectory WriteLine(string fileName, string text) { writer.WriteLine(text); writer.Flush(); + writer.Close(); } return this; @@ -46,7 +52,9 @@ public void DeleteFile(string fileName) public string Map(string path) { - return Path.Combine(_name, path); + var mapPath = Path.Combine(_name, path); + Debug.WriteLine($"Map [{mapPath.Length}]: {mapPath}"); + return mapPath; } } } diff --git a/test/ScriptCs.Tests.Acceptance/Support/ScriptCsExe.cs b/test/ScriptCs.Tests.Acceptance/Support/ScriptCsExe.cs index e757c621..d2013d71 100644 --- a/test/ScriptCs.Tests.Acceptance/Support/ScriptCsExe.cs +++ b/test/ScriptCs.Tests.Acceptance/Support/ScriptCsExe.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; + using System.Text; public static class ScriptCsExe { @@ -41,6 +42,11 @@ public static string Run(IEnumerable args, ScenarioDirectory directory) return Run(null, true, args, Enumerable.Empty(), directory); } + public static string Run(IEnumerable args, bool debug, ScenarioDirectory directory) + { + return Run(null, debug, args, Enumerable.Empty(), directory); + } + public static string Run(string scriptName, ScenarioDirectory directory) { return Run(scriptName, true, Enumerable.Empty(), Enumerable.Empty(), directory); @@ -67,7 +73,7 @@ public static string Run( debug && !args.Select(arg => arg.Trim().ToUpperInvariant()).Contains("-DEBUG") && !args.Select(arg => arg.Trim().ToUpperInvariant()).Contains("-D") - ? new[] { "-debug" } + ? new[] { "--debug" } : new string[0]; return Execute( @@ -96,17 +102,17 @@ public static string Install(string package, ScenarioDirectory directory) writer.Flush(); } - return Execute(new[] { "-install", package }, Enumerable.Empty(), directory); + return Execute(new[] { "install", package }, Enumerable.Empty(), directory); } public static string Save(ScenarioDirectory directory) { - return Execute(new[] { "-save" }, Enumerable.Empty(), directory); + return Execute(new[] { "install", "--save" }, Enumerable.Empty(), directory); } public static string Clean(ScenarioDirectory directory) { - return Execute(new[] { "-clean" }, Enumerable.Empty(), directory); + return Execute(new[] { "install", "--clean" }, Enumerable.Empty(), directory); } private static string Execute( @@ -127,7 +133,7 @@ private static string Execute( #endif var exe = Path.GetFullPath( - Path.Combine("..", "..", "..", "..", "src", "ScriptCs", "bin", config, "scriptcs.exe")); + Path.Combine("..", "..", "..", "..", "..", "src", "ScriptCs", "bin", config, "net461", "scriptcs.exe")); var info = new ProcessStartInfo { @@ -141,7 +147,9 @@ private static string Execute( UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, - RedirectStandardError = true + RedirectStandardError = true, + StandardErrorEncoding = Encoding.UTF8, + StandardOutputEncoding = Encoding.UTF8 }; var result = info.Run(Path.GetFileName(directory.Name) + ".log"); if (result.Item1 != 0) diff --git a/test/ScriptCs.Tests.Acceptance/app.config b/test/ScriptCs.Tests.Acceptance/app.config new file mode 100644 index 00000000..74979dd4 --- /dev/null +++ b/test/ScriptCs.Tests.Acceptance/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/ScriptCs.Tests.Acceptance/packages.config b/test/ScriptCs.Tests.Acceptance/packages.config deleted file mode 100644 index 37fca1dd..00000000 --- a/test/ScriptCs.Tests.Acceptance/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/test/ScriptCs.Tests.Acceptance/xunit.runner.json b/test/ScriptCs.Tests.Acceptance/xunit.runner.json new file mode 100644 index 00000000..edb23212 --- /dev/null +++ b/test/ScriptCs.Tests.Acceptance/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "parallelizeTestCollections": false +} \ No newline at end of file diff --git a/test/ScriptCs.Tests/CleanCommandTests.cs b/test/ScriptCs.Tests/CleanCommandTests.cs index 505966ff..d49cfffc 100644 --- a/test/ScriptCs.Tests/CleanCommandTests.cs +++ b/test/ScriptCs.Tests/CleanCommandTests.cs @@ -1,9 +1,9 @@ using Moq; -using Ploeh.AutoFixture.Xunit; +using AutoFixture.Xunit2; using ScriptCs.Command; using ScriptCs.Contracts; using ScriptCs.Hosting; -using Xunit.Extensions; +using Xunit; namespace ScriptCs.Tests { diff --git a/test/ScriptCs.Tests/CommandFactoryTests.cs b/test/ScriptCs.Tests/CommandFactoryTests.cs index 5903bf64..31eaede7 100644 --- a/test/ScriptCs.Tests/CommandFactoryTests.cs +++ b/test/ScriptCs.Tests/CommandFactoryTests.cs @@ -1,6 +1,6 @@ using Moq; -using Ploeh.AutoFixture; -using Ploeh.AutoFixture.AutoMoq; +using AutoFixture; +using AutoFixture.AutoMoq; using ScriptCs.Command; using ScriptCs.Contracts; using ScriptCs.Hosting; @@ -62,6 +62,25 @@ public void ShouldInstallAndSaveWhenInstallFlagIsOn() compositeCommand.Commands[1].ShouldImplement(); } + [Fact] + public void ShouldExecuteLooseScriptWhenExecIsPassed() + { + // Arrange + var args = new Config + { + AllowPreRelease = false, + PackageName = null, + Eval = "foo" + }; + + // Act + var factory = new CommandFactory(CreateBuilder()); + var result = factory.CreateCommand(args, new string[0]); + + // Assert + result.ShouldImplement(); + } + [Fact] public void ShouldExecuteWhenScriptNameIsPassed() { diff --git a/test/ScriptCs.Tests/ConfigTests.cs b/test/ScriptCs.Tests/ConfigTests.cs index ccac40b8..040084d0 100644 --- a/test/ScriptCs.Tests/ConfigTests.cs +++ b/test/ScriptCs.Tests/ConfigTests.cs @@ -1,9 +1,11 @@ -using ScriptCs.Contracts; -using Should; +using Should; using Xunit.Extensions; namespace ScriptCs.Tests { + using ScriptCs.Contracts; + using Xunit; + public class ConfigTests { public class TheApplyMethod diff --git a/test/ScriptCs.Tests/ExecuteLooseScriptCommandTests.cs b/test/ScriptCs.Tests/ExecuteLooseScriptCommandTests.cs new file mode 100644 index 00000000..b9e17f2d --- /dev/null +++ b/test/ScriptCs.Tests/ExecuteLooseScriptCommandTests.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Moq; +using ScriptCs.Command; +using ScriptCs.Contracts; +using ScriptCs.Hosting; +using Should; +using Xunit; +using AutoFixture.Xunit2; + +namespace ScriptCs.Tests +{ + public class ExecuteLooseScriptCommandTests + { + public class ExecuteMethod + { + [Theory, ScriptCsAutoData] + public void LooseScriptExecCommandShouldInvokeWithScriptPassedFromArgs( + [Frozen] Mock fileSystem, + [Frozen] Mock executor, + [Frozen] Mock initializationServices, + [Frozen] Mock servicesBuilder, + ScriptServices services) + { + // arrange + var args = new Config { AllowPreRelease = false, PackageName = "", Eval = "foo", }; + + initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); + servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); + servicesBuilder.Setup(b => b.Build()).Returns(services); + + var factory = new CommandFactory(servicesBuilder.Object); + var sut = factory.CreateCommand(args, new string[0]); + + // act + sut.Execute(); + + // assert + executor.Verify( + i => i.Initialize( + It.IsAny>(), It.IsAny>()), Times.Once()); + + executor.Verify( + i => i.ExecuteScript(It.Is(x => x == "foo"), It.IsAny()), Times.Once()); + + executor.Verify( + i => i.Terminate(), Times.Once()); + } + + [Theory, ScriptCsAutoData] + public void NonManagedAssembliesAreExcluded( + [Frozen] Mock fileSystem, + [Frozen] Mock assemblyUtility, + [Frozen] Mock executor, + [Frozen] Mock initializationServices, + [Frozen] Mock servicesBuilder, + ScriptServices services) + { + // arrange + const string NonManaged = "non-managed.dll"; + + var args = new Config { AllowPreRelease = false, PackageName = "", Eval = "foo", }; + + fileSystem.Setup( + x => x.EnumerateFiles(It.IsAny(), It.IsAny(), SearchOption.AllDirectories)) + .Returns(new[] { "managed.dll", NonManaged }); + + assemblyUtility.Setup(x => x.IsManagedAssembly(It.Is(y => y == NonManaged))).Returns(false); + initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); + servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); + servicesBuilder.Setup(b => b.Build()).Returns(services); + + var factory = new CommandFactory(servicesBuilder.Object); + var sut = factory.CreateCommand(args, new string[0]); + + // act + sut.Execute(); + + // assert + executor.Verify( + i => i.Initialize( + It.Is>(x => !x.Contains(NonManaged)), It.IsAny>()), + Times.Once()); + + executor.Verify( + i => i.ExecuteScript(It.Is(x => x == "foo"), It.IsAny()), Times.Once()); + + executor.Verify( + i => i.Terminate(), Times.Once()); + } + + [Theory, ScriptCsAutoData] + public void ShouldReturnErrorIfThereIsCompileException( + [Frozen] Mock fileSystem, + [Frozen] Mock executor, + [Frozen] TestLogProvider logProvider, + [Frozen] Mock initializationServices, + [Frozen] Mock servicesBuilder, + ScriptServices services) + { + // arrange + var args = new Config + { + AllowPreRelease = false, + PackageName = "", + Eval = "foo" + }; + + executor.Setup(i => i.ExecuteScript(It.IsAny(), It.IsAny())) + .Returns(new ScriptResult(compilationException: new Exception("test"))); + + initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); + servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); + servicesBuilder.Setup(b => b.Build()).Returns(services); + + var factory = new CommandFactory(servicesBuilder.Object); + var sut = factory.CreateCommand(args, new string[0]); + + // act + var result = sut.Execute(); + + // assert + result.ShouldEqual(CommandResult.Error); + logProvider.Output.ShouldContain("ERROR:"); + } + + [Theory, ScriptCsAutoData] + public void ShouldReturnErrorIfThereIsExecutionException( + [Frozen] Mock fileSystem, + [Frozen] Mock executor, + [Frozen] TestLogProvider logProvider, + [Frozen] Mock initializationServices, + [Frozen] Mock servicesBuilder, + ScriptServices services) + { + // arrange + var args = new Config + { + AllowPreRelease = false, + PackageName = "", + Eval = "foo" + }; + + executor.Setup(i => i.ExecuteScript(It.IsAny(), It.IsAny())) + .Returns(new ScriptResult(executionException: new Exception("test"))); + + initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); + servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); + servicesBuilder.Setup(b => b.Build()).Returns(services); + + var factory = new CommandFactory(servicesBuilder.Object); + var sut = factory.CreateCommand(args, new string[0]); + + // act + var result = sut.Execute(); + + // assert + result.ShouldEqual(CommandResult.Error); + logProvider.Output.ShouldContain("ERROR:"); + } + + [Theory, ScriptCsAutoData] + public void ShouldReturnErrorIfTheScriptIsIncomplete( + [Frozen] Mock fileSystem, + [Frozen] Mock executor, + [Frozen] TestLogProvider logProvider, + [Frozen] Mock initializationServices, + [Frozen] Mock servicesBuilder, + ScriptServices services) + { + // arrange + var args = new Config { Eval = "foo" }; + + executor.Setup(i => i.ExecuteScript(It.IsAny(), It.IsAny())) + .Returns(ScriptResult.Incomplete); + + initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); + servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); + servicesBuilder.Setup(b => b.Build()).Returns(services); + + var factory = new CommandFactory(servicesBuilder.Object); + var sut = factory.CreateCommand(args, new string[0]); + + // act + var result = sut.Execute(); + + // assert + result.ShouldEqual(CommandResult.Error); + logProvider.Output.ShouldContain("ERROR:"); + } + + [Theory, ScriptCsAutoData] + public void ShouldComposeScripts([Frozen] Mock fileSystem, Mock composer) + { + var cmd = new ExecuteLooseScriptCommand( + null, + null, + fileSystem.Object, + new Mock().Object, + new Mock().Object, + new TestLogProvider(), + new Mock().Object, + composer.Object); + + cmd.Execute(); + + composer.Verify(c => c.Compose(It.IsAny(), null)); + } + + + } + } +} diff --git a/test/ScriptCs.Tests/ExecuteReplCommandTests.cs b/test/ScriptCs.Tests/ExecuteReplCommandTests.cs index 74059b45..0607feb9 100644 --- a/test/ScriptCs.Tests/ExecuteReplCommandTests.cs +++ b/test/ScriptCs.Tests/ExecuteReplCommandTests.cs @@ -2,13 +2,11 @@ using System.Collections.Generic; using System.Text; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Command; using ScriptCs.Contracts; using ScriptCs.Hosting; -using ScriptCs.Logging; -using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -26,11 +24,8 @@ public void ShouldPromptForInput( { // arrange var config = new Config { Repl = true }; - var readLines = 0; - var builder = new StringBuilder(); - console.Setup(x => x.ReadLine()).Callback(() => readLines++).Throws(new Exception()); - console.Setup(x => x.Write(It.IsAny())).Callback(value => builder.Append(value)); + console.Setup(x => x.ReadLine(It.IsAny())).Returns((string)null); initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); servicesBuilder.SetupGet(b => b.ConsoleInstance).Returns(console.Object); @@ -43,8 +38,7 @@ public void ShouldPromptForInput( sut.Execute(); // assert - builder.ToString().EndsWith("> ").ShouldBeTrue(); - readLines.ShouldEqual(1); + console.Verify(x=>x.ReadLine("> ")); } [Theory, ScriptCsAutoData] @@ -66,7 +60,7 @@ public void WhenPassedAScript_ShouldPressedReplWithScript( It.IsAny>(), It.IsAny())); - console.Setup(x => x.ReadLine()).Throws(new Exception()); + console.Setup(x => x.ReadLine("")).Throws(new Exception()); initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); servicesBuilder.Setup(b => b.Build()).Returns(services); @@ -93,7 +87,7 @@ public void WhenNotPassedAScript_ShouldNotCallTheEngineAutomatically( // arrange var config = new Config { Repl = true }; - console.Setup(x => x.ReadLine()).Throws(new Exception()); + console.Setup(x => x.ReadLine("")).Throws(new Exception()); initializationServices.Setup(i => i.GetFileSystem()).Returns(fileSystem.Object); servicesBuilder.SetupGet(b => b.InitializationServices).Returns(initializationServices.Object); servicesBuilder.Setup(b => b.Build()).Returns(services); @@ -115,35 +109,6 @@ public void WhenNotPassedAScript_ShouldNotCallTheEngineAutomatically( Times.Never()); } - [Theory, ScriptCsAutoData] - public void MigratesTheFileSystem( - [Frozen] Mock fileSystem, - [Frozen] Mock console, - [Frozen] Mock fileSystemMigrator, - IScriptLibraryComposer composer) - { - // arrange - console.Setup(c => c.ReadLine()).Throws(new Exception()); - var sut = new ExecuteReplCommand( - null, - null, - fileSystem.Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - console.Object, - new Mock().Object, - fileSystemMigrator.Object, - composer - ); - - // act - sut.Execute(); - - // assert - fileSystemMigrator.Verify(m => m.Migrate(), Times.Once); - } - [Theory, ScriptCsAutoData] public void ShouldComposeScripts([Frozen] Mock fileSystem, Mock composer) { @@ -153,9 +118,8 @@ public void ShouldComposeScripts([Frozen] Mock fileSystem, Mock().Object, new Mock().Object, - new Mock().Object, + new TestLogProvider(), new Mock().Object, - new Mock().Object, composer.Object); cmd.Execute(); diff --git a/test/ScriptCs.Tests/ExecuteScriptCommandTests.cs b/test/ScriptCs.Tests/ExecuteScriptCommandTests.cs index c0562597..9c3c302f 100644 --- a/test/ScriptCs.Tests/ExecuteScriptCommandTests.cs +++ b/test/ScriptCs.Tests/ExecuteScriptCommandTests.cs @@ -3,13 +3,12 @@ using System.IO; using System.Linq; using Moq; -using Ploeh.AutoFixture.Xunit; using ScriptCs.Command; using ScriptCs.Contracts; using ScriptCs.Hosting; -using ScriptCs.Logging; using Should; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { @@ -96,7 +95,7 @@ public void NonManagedAssembliesAreExcluded( public void ShouldReturnErrorIfThereIsCompileException( [Frozen] Mock fileSystem, [Frozen] Mock executor, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, [Frozen] Mock initializationServices, [Frozen] Mock servicesBuilder, ScriptServices services) @@ -124,14 +123,14 @@ public void ShouldReturnErrorIfThereIsCompileException( // assert result.ShouldEqual(CommandResult.Error); - ((TestLogger)log).Output.ShouldContain("ERROR:"); + logProvider.Output.ShouldContain("ERROR:"); } [Theory, ScriptCsAutoData] public void ShouldReturnErrorIfThereIsExecutionException( [Frozen] Mock fileSystem, [Frozen] Mock executor, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, [Frozen] Mock initializationServices, [Frozen] Mock servicesBuilder, ScriptServices services) @@ -159,14 +158,14 @@ public void ShouldReturnErrorIfThereIsExecutionException( // assert result.ShouldEqual(CommandResult.Error); - ((TestLogger)log).Output.ShouldContain("ERROR:"); + logProvider.Output.ShouldContain("ERROR:"); } [Theory, ScriptCsAutoData] public void ShouldReturnErrorIfTheScriptIsIncomplete( [Frozen] Mock fileSystem, [Frozen] Mock executor, - [Frozen] ILog log, + [Frozen] TestLogProvider logProvider, [Frozen] Mock initializationServices, [Frozen] Mock servicesBuilder, ScriptServices services) @@ -189,30 +188,7 @@ public void ShouldReturnErrorIfTheScriptIsIncomplete( // assert result.ShouldEqual(CommandResult.Error); - ((TestLogger)log).Output.ShouldContain("ERROR:"); - } - - [Theory, ScriptCsAutoData] - public void MigratesTheFileSystem( - [Frozen] Mock fileSystem, [Frozen] Mock fileSystemMigrator) - { - // arrange - var sut = new ExecuteScriptCommand( - null, - null, - fileSystem.Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - fileSystemMigrator.Object, - new Mock().Object); - - // act - sut.Execute(); - - // assert - fileSystemMigrator.Verify(m => m.Migrate(), Times.Once); + logProvider.Output.ShouldContain("ERROR:"); } [Theory, ScriptCsAutoData] @@ -224,9 +200,8 @@ public void ShouldComposeScripts([Frozen] Mock fileSystem, Mock().Object, new Mock().Object, - new Mock().Object, + new TestLogProvider(), new Mock().Object, - new Mock().Object, composer.Object); cmd.Execute(); diff --git a/test/ScriptCs.Tests/InstallCommandTests.cs b/test/ScriptCs.Tests/InstallCommandTests.cs index 0ff33c93..cc1c5393 100644 --- a/test/ScriptCs.Tests/InstallCommandTests.cs +++ b/test/ScriptCs.Tests/InstallCommandTests.cs @@ -3,13 +3,13 @@ using System.Linq; using System.Runtime.Versioning; using Moq; -using Ploeh.AutoFixture; -using Ploeh.AutoFixture.AutoMoq; -using Ploeh.AutoFixture.Xunit; +using AutoFixture; +using AutoFixture.AutoMoq; using ScriptCs.Command; using ScriptCs.Contracts; using ScriptCs.Hosting; -using Xunit.Extensions; +using Xunit; +using AutoFixture.Xunit2; namespace ScriptCs.Tests { diff --git a/test/ScriptCs.Tests/Properties/AssemblyInfo.cs b/test/ScriptCs.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3180a546..00000000 --- a/test/ScriptCs.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("ScriptCs.Tests")] -[assembly: AssemblyDescription("")] - -[assembly: Guid("96733f0d-6fa4-4f6d-8f69-e4718eaf48b7")] diff --git a/test/ScriptCs.Tests/ScriptCs.Tests.csproj b/test/ScriptCs.Tests/ScriptCs.Tests.csproj index c5de1846..25bde541 100644 --- a/test/ScriptCs.Tests/ScriptCs.Tests.csproj +++ b/test/ScriptCs.Tests/ScriptCs.Tests.csproj @@ -1,119 +1,27 @@ - - - + - Debug - AnyCPU - {4D6A2A55-BB17-40CB-9567-EAFF80BEFDCE} - Library - ScriptCs.Tests - ScriptCs.Tests - Properties - 512 - v4.5 - - ..\..\ - ..\..\ScriptCs.Test.ruleset - 1701 + net461 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - Properties\CommonAssemblyInfo.cs - - - Properties\CommonVersionInfo.cs - - - ScriptCsAutoDataAttribute.cs - - - ScriptCsMoqCustomization.cs - - - TestLogger.cs - - - - - - - - - - - - + + + - + + + + + + - - ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll - - - ..\..\packages\AutoFixture.3.18.8\lib\net40\Ploeh.AutoFixture.dll - - - ..\..\packages\AutoFixture.AutoMoq.3.18.8\lib\net40\Ploeh.AutoFixture.AutoMoq.dll - - - ..\..\packages\AutoFixture.Xunit.3.18.8\lib\net40\Ploeh.AutoFixture.Xunit.dll - - - ..\..\packages\Should.1.1.20\lib\Should.dll - - - - ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll - - - ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll - + + + + - - {6049e205-8b5f-4080-b023-70600e51fd64} - ScriptCs.Contracts - - - {e590e710-e159-48e6-a3e6-1a83d3fe732c} - ScriptCs.Core - - - {9aef2d95-87fb-4829-b384-34bfe076d531} - ScriptCs.Hosting - - - {25080671-1a80-4041-b9c7-260578ff4849} - ScriptCs - + - - - - - - This project references NuGet package(s) that are missing on this computer. Enable 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/test/ScriptCs.Tests/ScriptCsArgsTests.cs b/test/ScriptCs.Tests/ScriptCsArgsTests.cs deleted file mode 100644 index 4c5581c7..00000000 --- a/test/ScriptCs.Tests/ScriptCsArgsTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Should; -using Xunit; - -namespace ScriptCs.Tests -{ - public class ScriptCsArgsTests - { - public class TheParseMethod - { - [Fact] - public void ShouldSetVersionIfPackageVersionNumberFollowsPackageToInstallName() - { - string[] args = { "-install", "glimpse.scriptcs", "1.0.1", "-pre" }; - - var result = ScriptCsArgs.Parse(args); - - result.Install.ShouldEqual("glimpse.scriptcs"); - result.PackageVersion.ShouldEqual("1.0.1"); - result.AllowPreRelease.ShouldBeTrue(); - } - - [Fact] - public void ShouldSetVersionIfPackageVersionNumberSpecifiedExplicitly() - { - string[] args = { "-install", "glimpse.scriptcs", "-packageversion", "1.0.1", "-pre" }; - - var result = ScriptCsArgs.Parse(args); - - result.Install.ShouldEqual("glimpse.scriptcs"); - result.PackageVersion.ShouldEqual("1.0.1"); - result.AllowPreRelease.ShouldBeTrue(); - } - } - } -} diff --git a/test/ScriptCs.Tests/app.config b/test/ScriptCs.Tests/app.config index 288bbd3e..8cea6cbd 100644 --- a/test/ScriptCs.Tests/app.config +++ b/test/ScriptCs.Tests/app.config @@ -2,14 +2,54 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/test/ScriptCs.Tests/packages.config b/test/ScriptCs.Tests/packages.config deleted file mode 100644 index f815c20c..00000000 --- a/test/ScriptCs.Tests/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/test/ScriptCsAutoDataAttribute.cs b/test/ScriptCsAutoDataAttribute.cs index 9fae86d6..cdbc5b65 100644 --- a/test/ScriptCsAutoDataAttribute.cs +++ b/test/ScriptCsAutoDataAttribute.cs @@ -1,7 +1,7 @@ -using Ploeh.AutoFixture; -using Ploeh.AutoFixture.AutoMoq; -using Ploeh.AutoFixture.Xunit; -using Xunit.Extensions; +using AutoFixture; +using AutoFixture.AutoMoq; +using AutoFixture.Xunit2; +using Xunit; namespace ScriptCs.Tests { @@ -10,10 +10,16 @@ public class ScriptCsAutoDataAttribute : CompositeDataAttribute public ScriptCsAutoDataAttribute(params object[] values) : base( new InlineDataAttribute(values), - new AutoDataAttribute( - new Fixture().Customize(new ScriptCsMoqCustomization())) + new InternalScriptCsAutoDataAttribute() ) { } } + + public class InternalScriptCsAutoDataAttribute : AutoDataAttribute + { + public InternalScriptCsAutoDataAttribute() : base(() => new Fixture().Customize(new ScriptCsMoqCustomization())) + { + } + } } \ No newline at end of file diff --git a/test/ScriptCsMoqCustomization.cs b/test/ScriptCsMoqCustomization.cs index bb705ffe..51467257 100644 --- a/test/ScriptCsMoqCustomization.cs +++ b/test/ScriptCsMoqCustomization.cs @@ -1,8 +1,8 @@ -using Moq; -using Ploeh.AutoFixture; -using Ploeh.AutoFixture.AutoMoq; +using System.Collections.Generic; +using Moq; +using AutoFixture; +using AutoFixture.AutoMoq; using ScriptCs.Contracts; -using ScriptCs.Logging; namespace ScriptCs.Tests { @@ -13,28 +13,70 @@ void ICustomization.Customize(IFixture fixture) this.Customize(fixture); fixture.Register(() => - { - var fileSystem = new Mock(); - fileSystem.SetupGet(f => f.PackagesFile).Returns("scriptcs_packages.config"); - fileSystem.SetupGet(f => f.PackagesFolder).Returns("scriptcs_packages"); - fileSystem.SetupGet(f => f.BinFolder).Returns("scriptcs_bin"); - fileSystem.SetupGet(f => f.DllCacheFolder).Returns(".scriptcs_cache"); - fileSystem.SetupGet(f => f.NugetFile).Returns("scriptcs_nuget.config"); - fileSystem.SetupGet(f => f.CurrentDirectory).Returns("workingdirectory"); - fileSystem.Setup(f => f.FileExists(@"workingdirectory\scriptcs_packages\PackageScripts.csx")).Returns(false); - fileSystem.Setup(f => f.DirectoryExists(@"workingdirectory\scriptcs_packages")).Returns(true); - fileSystem.Setup(f => f.GetWorkingDirectory(It.IsAny())).Returns("workingdirectory"); - return fileSystem; - }); + { + var fileSystem = new Mock(); + fileSystem.SetupGet(f => f.PackagesFile).Returns("scriptcs_packages.config"); + fileSystem.SetupGet(f => f.PackagesFolder).Returns("scriptcs_packages"); + fileSystem.SetupGet(f => f.BinFolder).Returns("scriptcs_bin"); + fileSystem.SetupGet(f => f.DllCacheFolder).Returns(".scriptcs_cache"); + fileSystem.SetupGet(f => f.NugetFile).Returns("scriptcs_nuget.config"); + fileSystem.SetupGet(f => f.CurrentDirectory).Returns("workingdirectory"); + fileSystem.Setup(f => f.FileExists(@"workingdirectory\scriptcs_packages\PackageScripts.csx")) + .Returns(false); + fileSystem.Setup(f => f.DirectoryExists(@"workingdirectory\scriptcs_packages")).Returns(true); + fileSystem.Setup(f => f.GetWorkingDirectory(It.IsAny())).Returns("workingdirectory"); + return fileSystem; + }); fixture.Register(() => - { - var composer = new Mock(); - composer.SetupGet(c => c.ScriptLibrariesFile).Returns("ScriptLibraries.csx"); - return composer; - }); + { + var composer = new Mock(); + composer.SetupGet(c => c.ScriptLibrariesFile).Returns("ScriptLibraries.csx"); + return composer; + }); - fixture.Register(fixture.Create); + var logProvider = new TestLogProvider(); + fixture.Register(() => logProvider); + fixture.Register(() => logProvider); + + fixture.Register(() => new AppDomainAssemblyResolver( + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create>())); + + fixture.Register(() => new ScriptLibraryComposer( + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create())); + + fixture.Register(() => new ScriptServices( + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create>(), + fixture.Create(), + fixture.Create(), + fixture.Create())); + + fixture.Register(() => new ScriptExecutor( + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + fixture.Create(), + new ScriptInfo())); } } } diff --git a/test/TestLogProvider.cs b/test/TestLogProvider.cs new file mode 100644 index 00000000..9348134a --- /dev/null +++ b/test/TestLogProvider.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; +using System.Text; + +namespace ScriptCs.Tests +{ + using ScriptCs.Contracts; + + public class TestLogProvider : ILogProvider + { + private static readonly Disposable disposable = new Disposable(); + private readonly StringBuilder _output = new StringBuilder(); + + public string Output + { + get { return _output.ToString(); } + } + + public Logger GetLogger(string name) + { + return Log; + } + + public IDisposable OpenNestedContext(string message) + { + return disposable; + } + + public IDisposable OpenMappedContext(string key, string value) + { + return disposable; + } + + private bool Log( + LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) + { + if (messageFunc != null) + { + var line = string.Format( + CultureInfo.InvariantCulture, + "{0}: {1}", + logLevel.ToString().ToUpper(), + string.Format(CultureInfo.InvariantCulture, messageFunc(), formatParameters)); + + _output.AppendLine(line); + } + + return true; + } + + private sealed class Disposable : IDisposable + { + public void Dispose() + { + } + } + } +} diff --git a/test/TestLogger.cs b/test/TestLogger.cs deleted file mode 100644 index d9796c96..00000000 --- a/test/TestLogger.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using ScriptCs.Logging; - -namespace ScriptCs.Tests -{ - using System.Globalization; - using System.Text; - - public class TestLogger : ILog - { - private readonly StringBuilder _output = new StringBuilder(); - - public bool Log( - LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) - { - if (messageFunc != null) - { - var message = string.Format(CultureInfo.InvariantCulture, messageFunc(), formatParameters); - var text = string.Format(CultureInfo.InvariantCulture, "{0}: {1}", logLevel.ToString().ToUpper(), message); - this._output.AppendLine(text); - } - - return true; - } - - public string Output - { - get { return this._output.ToString(); } - } - } -}