From 91e94b13536e3b07451aed78ac56ba327e074597 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Fri, 15 Jul 2016 11:54:24 -0700 Subject: [PATCH] automate the autobahn test suite (#101) However, we only run that automation if the Autobahn test suite runner (wstest) is installed. After this is in and we can verify it on the CI we'll look at making it mandatory for building the repo. Also includes test applications and scripts for running Autobahn suites against arbitrary ASP.NET 4.6, ASP.NET Core and HttpListener applications. --- .gitignore | 3 +- .travis.yml | 5 +- README.md | 6 + WebSockets.sln | 69 ++++---- appveyor.yml | 3 + makefile.shade | 15 ++ .../AutobahnTestAppAspNet4.csproj | 129 +++++++++++++++ .../AutobahnTestAppAspNet4/EchoSocket.ashx | 1 + .../AutobahnTestAppAspNet4/EchoSocket.ashx.cs | 46 ++++++ .../Properties/AssemblyInfo.cs | 35 +++++ .../AutobahnTestAppAspNet4/Web.Debug.config | 30 ++++ .../AutobahnTestAppAspNet4/Web.Release.config | 31 ++++ samples/AutobahnTestAppAspNet4/Web.config | 21 +++ .../AutobahnTestAppAspNet4/packages.config | 5 + .../AutobahnTestAppAspNet4/wstest-spec.json | 14 ++ .../AutobahnTestAppHttpListener/App.config | 6 + .../AutobahnTestAppHttpListener.csproj | 60 +++++++ .../AutobahnTestAppHttpListener/Program.cs | 106 +++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++ {test => samples}/TestClient/App.config | 0 {test => samples}/TestClient/Program.cs | 0 .../TestClient/Properties/AssemblyInfo.cs | 0 {test => samples}/TestClient/TestClient.xproj | 0 {test => samples}/TestClient/project.json | 0 {test => samples}/TestServer/App.config | 0 {test => samples}/TestServer/Program.cs | 0 .../TestServer/Properties/AssemblyInfo.cs | 0 .../TestServer/TestServer.csproj | 0 .../AutobahnTestApp.xproj} | 16 +- test/AutobahnTestApp/Program.cs | 39 +++++ .../Properties/launchSettings.json | 42 +++++ test/AutobahnTestApp/README.md | 14 ++ test/AutobahnTestApp/Startup.cs | 54 +++++++ .../TestResources/testCert.pfx | Bin 0 -> 2483 bytes .../TestResources/testCert.txt | 3 + test/AutobahnTestApp/project.json | 55 +++++++ .../scripts/RunAutobahnTests.ps1 | 30 ++++ .../scripts/autobahn.spec.json | 14 ++ .../web.config | 11 +- test/AutobahnTestClient/Program.cs | 107 ------------- test/AutobahnTestClient/ReadMe.txt | 23 --- test/AutobahnTestClient/project.json | 11 -- test/AutobahnTestServer/ReadMe.txt | 25 --- test/AutobahnTestServer/Startup.cs | 84 ---------- test/AutobahnTestServer/project.json | 36 ----- ...ft.AspNetCore.WebSockets.Client.Test.xproj | 3 + ....AspNetCore.WebSockets.Protocol.Test.xproj | 3 + .../Autobahn/AutobahnCaseResult.cs | 30 ++++ .../Autobahn/AutobahnExpectations.cs | 88 +++++++++++ .../Autobahn/AutobahnResult.cs | 24 +++ .../Autobahn/AutobahnServerResult.cs | 39 +++++ .../Autobahn/AutobahnSpec.cs | 61 ++++++++ .../Autobahn/AutobahnTester.cs | 148 ++++++++++++++++++ .../Autobahn/Executable.cs | 55 +++++++ .../Autobahn/Expectation.cs | 11 ++ .../Autobahn/ServerSpec.cs | 23 +++ .../Autobahn/Wstest.cs | 22 +++ .../AutobahnTests.cs | 104 ++++++++++++ .../Helpers.cs | 29 ++++ ...t.AspNetCore.WebSockets.Server.Test.xproj} | 9 +- .../Properties/AssemblyInfo.cs | 19 +++ .../SkipIfWsTestNotPresentAttribute.cs | 13 ++ .../project.json | 23 +++ 63 files changed, 1563 insertions(+), 326 deletions(-) create mode 100644 makefile.shade create mode 100644 samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj create mode 100644 samples/AutobahnTestAppAspNet4/EchoSocket.ashx create mode 100644 samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs create mode 100644 samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs create mode 100644 samples/AutobahnTestAppAspNet4/Web.Debug.config create mode 100644 samples/AutobahnTestAppAspNet4/Web.Release.config create mode 100644 samples/AutobahnTestAppAspNet4/Web.config create mode 100644 samples/AutobahnTestAppAspNet4/packages.config create mode 100644 samples/AutobahnTestAppAspNet4/wstest-spec.json create mode 100644 samples/AutobahnTestAppHttpListener/App.config create mode 100644 samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj create mode 100644 samples/AutobahnTestAppHttpListener/Program.cs create mode 100644 samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs rename {test => samples}/TestClient/App.config (100%) rename {test => samples}/TestClient/Program.cs (100%) rename {test => samples}/TestClient/Properties/AssemblyInfo.cs (100%) rename {test => samples}/TestClient/TestClient.xproj (100%) rename {test => samples}/TestClient/project.json (100%) rename {test => samples}/TestServer/App.config (100%) rename {test => samples}/TestServer/Program.cs (100%) rename {test => samples}/TestServer/Properties/AssemblyInfo.cs (100%) rename {test => samples}/TestServer/TestServer.csproj (100%) rename test/{AutobahnTestServer/AutobahnTestServer.xproj => AutobahnTestApp/AutobahnTestApp.xproj} (58%) create mode 100644 test/AutobahnTestApp/Program.cs create mode 100644 test/AutobahnTestApp/Properties/launchSettings.json create mode 100644 test/AutobahnTestApp/README.md create mode 100644 test/AutobahnTestApp/Startup.cs create mode 100644 test/AutobahnTestApp/TestResources/testCert.pfx create mode 100644 test/AutobahnTestApp/TestResources/testCert.txt create mode 100644 test/AutobahnTestApp/project.json create mode 100644 test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 create mode 100644 test/AutobahnTestApp/scripts/autobahn.spec.json rename test/{AutobahnTestServer => AutobahnTestApp}/web.config (57%) delete mode 100644 test/AutobahnTestClient/Program.cs delete mode 100644 test/AutobahnTestClient/ReadMe.txt delete mode 100644 test/AutobahnTestClient/project.json delete mode 100644 test/AutobahnTestServer/ReadMe.txt delete mode 100644 test/AutobahnTestServer/Startup.cs delete mode 100644 test/AutobahnTestServer/project.json create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnCaseResult.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnExpectations.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnResult.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnServerResult.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnSpec.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnTester.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Executable.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Expectation.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/ServerSpec.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Wstest.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/AutobahnTests.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Helpers.cs rename test/{AutobahnTestClient/AutobahnTestClient.xproj => Microsoft.AspNetCore.WebSockets.Server.Test/Microsoft.AspNetCore.WebSockets.Server.Test.xproj} (66%) create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/Properties/AssemblyInfo.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/SkipIfWsTestNotPresentAttribute.cs create mode 100644 test/Microsoft.AspNetCore.WebSockets.Server.Test/project.json diff --git a/.gitignore b/.gitignore index 308fec5350..d510684e27 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ nuget.exe project.lock.json /.vs/ .testPublish/ -.build/ \ No newline at end of file +.build/ +autobahnreports/ diff --git a/.travis.yml b/.travis.yml index be2e4ad775..a2b7cd463b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,11 @@ install: - make install - export LD_LIBRARY_PATH="$HOME/libuvinstall/lib" - cd $OLDPWD + - sudo pip install autobahntestsuite "six>=1.9.0" mono: - 4.0.5 +python: + - "2.7" os: - linux - osx @@ -33,6 +36,6 @@ branches: - dev - /^(.*\/)?ci-.*$/ before_install: - - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; brew link --force openssl; fi + - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl python; brew link --force openssl; fi script: - ./build.sh --quiet verify diff --git a/README.md b/README.md index 5dea9440f3..db96812f49 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,10 @@ Contains a managed implementation of the WebSocket protocol, along with client a This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. +## System Requirements +This repo has a few special system requirements/prerequisites. + +1. Windows IIS Express tests require IIS Express 10 and Windows 8 for WebSockets support +2. HttpListener/ASP.NET 4.6 samples require at least Windows 8 +3. Autobahn Test Suite requires special installation see the README.md in test/AutobahnTestApp diff --git a/WebSockets.sln b/WebSockets.sln index a7b3697b81..a79504e978 100644 --- a/WebSockets.sln +++ b/WebSockets.sln @@ -1,10 +1,7 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22216.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer", "test\TestServer\TestServer.csproj", "{4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2C7947A5-9FBD-4267-97C1-2D726D7B3BAF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C45106D0-76C8-4776-A140-F7DD83CA2958}" @@ -15,8 +12,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSoc EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSockets.Protocol.Test", "test\Microsoft.AspNetCore.WebSockets.Protocol.Test\Microsoft.AspNetCore.WebSockets.Protocol.Test.xproj", "{62A07A24-4D06-4DDA-B6BF-02D0C9CB7D32}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TestClient", "test\TestClient\TestClient.xproj", "{8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSockets.Client", "src\Microsoft.AspNetCore.WebSockets.Client\Microsoft.AspNetCore.WebSockets.Client.xproj", "{4A1C4875-AE21-4A78-979A-F0E4DF5EB518}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSockets.Client.Test", "test\Microsoft.AspNetCore.WebSockets.Client.Test\Microsoft.AspNetCore.WebSockets.Client.Test.xproj", "{6604D154-817F-4BC5-BE95-FF7E851179D9}" @@ -28,9 +23,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution global.json = global.json EndProjectSection EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AutobahnTestServer", "test\AutobahnTestServer\AutobahnTestServer.xproj", "{C03C43FE-9201-48A6-B434-AD67EF627D67}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TestClient", "samples\TestClient\TestClient.xproj", "{8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AutobahnTestClient", "test\AutobahnTestClient\AutobahnTestClient.xproj", "{BC4D2BB1-05A8-4816-8BC1-3A664F09EE32}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer", "samples\TestServer\TestServer.csproj", "{4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSockets.Server.Test", "test\Microsoft.AspNetCore.WebSockets.Server.Test\Microsoft.AspNetCore.WebSockets.Server.Test.xproj", "{E82D9F64-8AFA-4DCB-A842-2283FDA73BE8}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AutobahnTestApp", "test\AutobahnTestApp\AutobahnTestApp.xproj", "{9755F612-A155-4BDD-9E20-37ADE0B4B3BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutobahnTestAppAspNet4", "samples\AutobahnTestAppAspNet4\AutobahnTestAppAspNet4.csproj", "{72E3AB32-682F-42AF-B7C7-0B777244FF11}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutobahnTestAppHttpListener", "samples\AutobahnTestAppHttpListener\AutobahnTestAppHttpListener.csproj", "{B7246F23-6A4B-492F-AB61-292AA1A9E9D5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -38,10 +41,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.Build.0 = Release|Any CPU {E0C10DEC-3339-4A47-85BC-3100C5D34AD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E0C10DEC-3339-4A47-85BC-3100C5D34AD4}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0C10DEC-3339-4A47-85BC-3100C5D34AD4}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -50,10 +49,6 @@ Global {62A07A24-4D06-4DDA-B6BF-02D0C9CB7D32}.Debug|Any CPU.Build.0 = Debug|Any CPU {62A07A24-4D06-4DDA-B6BF-02D0C9CB7D32}.Release|Any CPU.ActiveCfg = Release|Any CPU {62A07A24-4D06-4DDA-B6BF-02D0C9CB7D32}.Release|Any CPU.Build.0 = Release|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.Build.0 = Release|Any CPU {4A1C4875-AE21-4A78-979A-F0E4DF5EB518}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A1C4875-AE21-4A78-979A-F0E4DF5EB518}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A1C4875-AE21-4A78-979A-F0E4DF5EB518}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -66,27 +61,45 @@ Global {78A097D0-C0A4-4AED-93E2-84A65392FB52}.Debug|Any CPU.Build.0 = Debug|Any CPU {78A097D0-C0A4-4AED-93E2-84A65392FB52}.Release|Any CPU.ActiveCfg = Release|Any CPU {78A097D0-C0A4-4AED-93E2-84A65392FB52}.Release|Any CPU.Build.0 = Release|Any CPU - {C03C43FE-9201-48A6-B434-AD67EF627D67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C03C43FE-9201-48A6-B434-AD67EF627D67}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C03C43FE-9201-48A6-B434-AD67EF627D67}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C03C43FE-9201-48A6-B434-AD67EF627D67}.Release|Any CPU.Build.0 = Release|Any CPU - {BC4D2BB1-05A8-4816-8BC1-3A664F09EE32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BC4D2BB1-05A8-4816-8BC1-3A664F09EE32}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC4D2BB1-05A8-4816-8BC1-3A664F09EE32}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BC4D2BB1-05A8-4816-8BC1-3A664F09EE32}.Release|Any CPU.Build.0 = Release|Any CPU + {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.Build.0 = Release|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.Build.0 = Release|Any CPU + {E82D9F64-8AFA-4DCB-A842-2283FDA73BE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E82D9F64-8AFA-4DCB-A842-2283FDA73BE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E82D9F64-8AFA-4DCB-A842-2283FDA73BE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E82D9F64-8AFA-4DCB-A842-2283FDA73BE8}.Release|Any CPU.Build.0 = Release|Any CPU + {9755F612-A155-4BDD-9E20-37ADE0B4B3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9755F612-A155-4BDD-9E20-37ADE0B4B3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9755F612-A155-4BDD-9E20-37ADE0B4B3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9755F612-A155-4BDD-9E20-37ADE0B4B3BA}.Release|Any CPU.Build.0 = Release|Any CPU + {72E3AB32-682F-42AF-B7C7-0B777244FF11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72E3AB32-682F-42AF-B7C7-0B777244FF11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72E3AB32-682F-42AF-B7C7-0B777244FF11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72E3AB32-682F-42AF-B7C7-0B777244FF11}.Release|Any CPU.Build.0 = Release|Any CPU + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} {E0C10DEC-3339-4A47-85BC-3100C5D34AD4} = {2C7947A5-9FBD-4267-97C1-2D726D7B3BAF} {62A07A24-4D06-4DDA-B6BF-02D0C9CB7D32} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} {4A1C4875-AE21-4A78-979A-F0E4DF5EB518} = {2C7947A5-9FBD-4267-97C1-2D726D7B3BAF} {6604D154-817F-4BC5-BE95-FF7E851179D9} = {C45106D0-76C8-4776-A140-F7DD83CA2958} {78A097D0-C0A4-4AED-93E2-84A65392FB52} = {2C7947A5-9FBD-4267-97C1-2D726D7B3BAF} - {C03C43FE-9201-48A6-B434-AD67EF627D67} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - {BC4D2BB1-05A8-4816-8BC1-3A664F09EE32} = {C45106D0-76C8-4776-A140-F7DD83CA2958} + {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} + {E82D9F64-8AFA-4DCB-A842-2283FDA73BE8} = {C45106D0-76C8-4776-A140-F7DD83CA2958} + {9755F612-A155-4BDD-9E20-37ADE0B4B3BA} = {C45106D0-76C8-4776-A140-F7DD83CA2958} + {72E3AB32-682F-42AF-B7C7-0B777244FF11} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} EndGlobalSection EndGlobal diff --git a/appveyor.yml b/appveyor.yml index 81cbe23e0c..5086ce1532 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,9 @@ branches: - /^(.*\/)?ci-.*$/ build_script: - build.cmd --quiet verify +install: + - set PATH=C:\Python27\scripts;%PATH% + - pip install autobahntestsuite clone_depth: 1 test: off deploy: off diff --git a/makefile.shade b/makefile.shade new file mode 100644 index 0000000000..ecfd6668cc --- /dev/null +++ b/makefile.shade @@ -0,0 +1,15 @@ +use namespace="System.IO" + +var VERSION='0.1' +var FULL_VERSION='0.1' +var AUTHORS='Microsoft' + +use-standard-lifecycle +k-standard-goals + +var AUTOBAHN_REPORTS_DIR='${Path.Combine(Directory.GetCurrentDirectory(), "artifacts", "autobahnreports")}' + +#set-autobahn-report-dir target='initialize' + @{ + E("AUTOBAHN_SUITES_REPORT_DIR", AUTOBAHN_REPORTS_DIR); + } diff --git a/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj b/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj new file mode 100644 index 0000000000..e778fb466b --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj @@ -0,0 +1,129 @@ + + + + + + + Debug + AnyCPU + + + 2.0 + {72E3AB32-682F-42AF-B7C7-0B777244FF11} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + AutobahnTestAppAspNet4 + AutobahnTestAppAspNet4 + v4.6.1 + true + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + EchoSocket.ashx + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 29392 + / + http://localhost:29392 + True + http://localhost:29392/EchoSocket.ashx + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/EchoSocket.ashx b/samples/AutobahnTestAppAspNet4/EchoSocket.ashx new file mode 100644 index 0000000000..7d018e48ed --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/EchoSocket.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="EchoSocket.ashx.cs" Class="AutobahnTestAppAspNet4.EchoSocket" %> diff --git a/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs b/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs new file mode 100644 index 0000000000..0b001f8715 --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; +using System.Web; + +namespace AutobahnTestAppAspNet4 +{ + /// + /// Summary description for EchoSocket + /// + public class EchoSocket : IHttpHandler + { + public bool IsReusable => false; + + public void ProcessRequest(HttpContext context) + { + if (context.IsWebSocketRequest) + { + context.AcceptWebSocketRequest(async socketContext => + { + await Echo(socketContext.WebSocket); + }); + } + else + { + context.Response.Write("Ready to accept WebSocket request at: " + context.Request.Url.ToString().Replace("https://", "wss://").Replace("http://", "ws://")); + context.Response.Flush(); + } + } + + private async Task Echo(WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + } +} \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs b/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..22e5a4f43d --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +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("AutobahnTestAppAspNet4")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AutobahnTestAppAspNet4")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[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("72e3ab32-682f-42af-b7c7-0b777244ff11")] + +// 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 Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/AutobahnTestAppAspNet4/Web.Debug.config b/samples/AutobahnTestAppAspNet4/Web.Debug.config new file mode 100644 index 0000000000..2e302f9f95 --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/Web.Release.config b/samples/AutobahnTestAppAspNet4/Web.Release.config new file mode 100644 index 0000000000..c35844462b --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/Web.config b/samples/AutobahnTestAppAspNet4/Web.config new file mode 100644 index 0000000000..9500f90d67 --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/Web.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/packages.config b/samples/AutobahnTestAppAspNet4/packages.config new file mode 100644 index 0000000000..5f629e51b2 --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppAspNet4/wstest-spec.json b/samples/AutobahnTestAppAspNet4/wstest-spec.json new file mode 100644 index 0000000000..d11521b04b --- /dev/null +++ b/samples/AutobahnTestAppAspNet4/wstest-spec.json @@ -0,0 +1,14 @@ +{ + "options": { "failByDrop": false }, + "outdir": "./bin/wstest-report", + "servers": [ + { + "agent": "ASP.NET 4", + "url": "ws://localhost:29392/EchoSocket.ashx", + "options": { "version": 18 } + } + ], + "cases": ["*"], + "exclude-cases": ["12.*", "13.*"], + "exclude-agent-cases": {} +} \ No newline at end of file diff --git a/samples/AutobahnTestAppHttpListener/App.config b/samples/AutobahnTestAppHttpListener/App.config new file mode 100644 index 0000000000..731f6de6c2 --- /dev/null +++ b/samples/AutobahnTestAppHttpListener/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj b/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj new file mode 100644 index 0000000000..943e30e4c3 --- /dev/null +++ b/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {B7246F23-6A4B-492F-AB61-292AA1A9E9D5} + Exe + Properties + AutobahnTestAppHttpListener + AutobahnTestAppHttpListener + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/AutobahnTestAppHttpListener/Program.cs b/samples/AutobahnTestAppHttpListener/Program.cs new file mode 100644 index 0000000000..3c965d1be6 --- /dev/null +++ b/samples/AutobahnTestAppHttpListener/Program.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +namespace AutobahnTestAppHttpListener +{ + class Program + { + // This app only works on Windows 8+ + static int Main(string[] args) + { + using (var listener = StartListener()) + { + if (listener == null) + { + return 1; + } + + var httpUrl = listener.Prefixes.Single(); + var wsUrl = httpUrl.Replace("http://", "ws://"); + + var stopTokenSource = new CancellationTokenSource(); + var task = Run(listener, wsUrl, stopTokenSource.Token); + + Console.CancelKeyPress += (sender, a) => + { + a.Cancel = true; + stopTokenSource.Cancel(); + }; + + Console.WriteLine($"HTTP: {httpUrl}"); + Console.WriteLine($"WS : {wsUrl}"); + Console.WriteLine("Press Ctrl-C to stop..."); + + task.Wait(); + } + return 0; + } + + private static async Task Run(HttpListener listener, string wsUrl, CancellationToken stopToken) + { + while (!stopToken.IsCancellationRequested) + { + try + { + var context = await listener.GetContextAsync(); + + if (context.Request.IsWebSocketRequest) + { + var socket = await context.AcceptWebSocketAsync(null); + await Echo(socket.WebSocket); + } + else + { + using (var writer = new StreamWriter(context.Response.OutputStream)) + { + await writer.WriteLineAsync($"Ready to accept WebSocket request at: {wsUrl}"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Request failed: {ex}"); + } + } + } + + private static async Task Echo(WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + + static HttpListener StartListener() + { + var port = 49152; // IANA recommends starting at port 49152 for dynamic ports + while (port < 65535) + { + HttpListener listener = new HttpListener(); + listener.Prefixes.Add($"http://localhost:{port}/"); + try + { + listener.Start(); + return listener; + } + catch + { + port++; + } + } + + Console.Error.WriteLine("Failed to find a free port!"); + return null; + } + } +} diff --git a/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs b/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f442cae407 --- /dev/null +++ b/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("AutobahnTestAppHttpListener")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AutobahnTestAppHttpListener")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[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("b7246f23-6a4b-492f-ab61-292aa1a9e9d5")] + +// 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/TestClient/App.config b/samples/TestClient/App.config similarity index 100% rename from test/TestClient/App.config rename to samples/TestClient/App.config diff --git a/test/TestClient/Program.cs b/samples/TestClient/Program.cs similarity index 100% rename from test/TestClient/Program.cs rename to samples/TestClient/Program.cs diff --git a/test/TestClient/Properties/AssemblyInfo.cs b/samples/TestClient/Properties/AssemblyInfo.cs similarity index 100% rename from test/TestClient/Properties/AssemblyInfo.cs rename to samples/TestClient/Properties/AssemblyInfo.cs diff --git a/test/TestClient/TestClient.xproj b/samples/TestClient/TestClient.xproj similarity index 100% rename from test/TestClient/TestClient.xproj rename to samples/TestClient/TestClient.xproj diff --git a/test/TestClient/project.json b/samples/TestClient/project.json similarity index 100% rename from test/TestClient/project.json rename to samples/TestClient/project.json diff --git a/test/TestServer/App.config b/samples/TestServer/App.config similarity index 100% rename from test/TestServer/App.config rename to samples/TestServer/App.config diff --git a/test/TestServer/Program.cs b/samples/TestServer/Program.cs similarity index 100% rename from test/TestServer/Program.cs rename to samples/TestServer/Program.cs diff --git a/test/TestServer/Properties/AssemblyInfo.cs b/samples/TestServer/Properties/AssemblyInfo.cs similarity index 100% rename from test/TestServer/Properties/AssemblyInfo.cs rename to samples/TestServer/Properties/AssemblyInfo.cs diff --git a/test/TestServer/TestServer.csproj b/samples/TestServer/TestServer.csproj similarity index 100% rename from test/TestServer/TestServer.csproj rename to samples/TestServer/TestServer.csproj diff --git a/test/AutobahnTestServer/AutobahnTestServer.xproj b/test/AutobahnTestApp/AutobahnTestApp.xproj similarity index 58% rename from test/AutobahnTestServer/AutobahnTestServer.xproj rename to test/AutobahnTestApp/AutobahnTestApp.xproj index fa9134b6ad..1e23714cd5 100644 --- a/test/AutobahnTestServer/AutobahnTestServer.xproj +++ b/test/AutobahnTestApp/AutobahnTestApp.xproj @@ -4,14 +4,22 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + + - c03c43fe-9201-48a6-b434-ad67ef627d67 + 9755f612-a155-4bdd-9e20-37ade0b4b3ba + AutobahnTestApp .\obj .\bin\ + v4.6.1 + 2.0 - - \ No newline at end of file + + + + + + diff --git a/test/AutobahnTestApp/Program.cs b/test/AutobahnTestApp/Program.cs new file mode 100644 index 0000000000..04098c88ab --- /dev/null +++ b/test/AutobahnTestApp/Program.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; + +namespace AutobahnTestApp +{ + public class Program + { + public static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .AddCommandLine(args) + .Build(); + + var builder = new WebHostBuilder() + .UseConfiguration(config) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup(); + + if (string.Equals(builder.GetSetting("server"), "Microsoft.AspNetCore.Server.WebListener", System.StringComparison.Ordinal)) + { + builder.UseWebListener(); + } + else + { + builder.UseKestrel(options => + { + var certPath = Path.Combine(AppContext.BaseDirectory, "TestResources", "testCert.pfx"); + options.UseHttps(certPath, "testPassword"); + }); + } + + var host = builder.Build(); + host.Run(); + } + } +} diff --git a/test/AutobahnTestApp/Properties/launchSettings.json b/test/AutobahnTestApp/Properties/launchSettings.json new file mode 100644 index 0000000000..c96436ad2a --- /dev/null +++ b/test/AutobahnTestApp/Properties/launchSettings.json @@ -0,0 +1,42 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6155/", + "sslPort": 44371 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "ManagedSockets" + } + }, + "AutobahnTestApp": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "ManagedSockets" + } + }, + "AutobahnTestApp (SSL)": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "https://localhost:5443", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "ManagedSockets" + } + }, + "WebListener": { + "commandName": "Project", + "commandLineArgs": "--server Microsoft.AspNetCore.Server.WebListener", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "ManagedSockets" + } + } + } +} \ No newline at end of file diff --git a/test/AutobahnTestApp/README.md b/test/AutobahnTestApp/README.md new file mode 100644 index 0000000000..ece563a39b --- /dev/null +++ b/test/AutobahnTestApp/README.md @@ -0,0 +1,14 @@ +# Autobahn Testing + +This application is used to provide the server for the [Autobahn Test Suite](http://autobahn.ws/testsuite) 'fuzzingclient' mode to test. It is a simple echo server that echos each frame recieved back to the client. + +In order to run these tests you must install CPython 2.7, Pip, and the test suite modules. You must also have +the `wstest` executable provided by the Autobahn Suite on the `PATH`. See http://autobahn.ws/testsuite/installation.html#installation for more info + +Once Autobahn is installed, launch this application in the desired configuration (in IIS Express, or using Kestrel directly) from Visual Studio and get the WebSocket URL from the HTTP response. Use that URL in place of `ws://server:1234` and invoke the `scripts\RunAutobahnTests.ps1` script in this project like so: + +``` +> .\scripts\RunAutobahnTests.ps1 -ServerUrl ws://server:1234 +``` + +By default, all cases are run and the report is written to the `autobahnreports` sub-directory of the directory in which you run the script. You can change either by using the `-Cases` and `-OutputDir` switches, use `.\script\RunAutobahnTests.ps1 -?` for help. \ No newline at end of file diff --git a/test/AutobahnTestApp/Startup.cs b/test/AutobahnTestApp/Startup.cs new file mode 100644 index 0000000000..67562d9ce0 --- /dev/null +++ b/test/AutobahnTestApp/Startup.cs @@ -0,0 +1,54 @@ +using System; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace AutobahnTestApp +{ + public class Startup + { + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + if (!env.IsEnvironment("NativeSockets")) + { + app.UseWebSockets(new WebSocketOptions + { + ReplaceFeature = true + }); + } + + app.Use(async (context, next) => + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Echo(webSocket); + } + else + { + var wsScheme = context.Request.IsHttps ? "wss" : "ws"; + var wsUrl = $"{wsScheme}://{context.Request.Host.Host}:{context.Request.Host.Port}{context.Request.Path}"; + await context.Response.WriteAsync($"Ready to accept a WebSocket request at: {wsUrl}"); + } + }); + + } + + private async Task Echo(WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + } +} diff --git a/test/AutobahnTestApp/TestResources/testCert.pfx b/test/AutobahnTestApp/TestResources/testCert.pfx new file mode 100644 index 0000000000000000000000000000000000000000..7118908c2d730670c16e9f8b2c532a262c951989 GIT binary patch literal 2483 zcmaKuc|27A8pqF>IWr86E&Q@(n=B)p$ug!;QVB6xij*z;uPLG!yCz#DQB)+9G$9m9 zQU)=DWXU?*EZIwG!+0d++P@yZ4Xhoagg?p6B~|Ue7tN=Ny=UD?x#1n1MTq z#c9MHh+D#gd|(a(cN}8i91v^=GcdgW3SmA$49p~gM-dys3jVWdg8+!iVL)pz1LDE5 zSb=|GAn(@R=(Ux!MfS9@}sFu-xDd zIt2+mqSq$glwy_6UNs<2?(qERU!gJ;5j}Pp&6trxG=wi)=@k(w2+fJVnc+qvXVzy(>Om4;L|^)R`t*3nTpAmEmTl(#i!RV#a0t#u6>Q9mY`-Nmcs7$XjXT7 zUmCD`O~_j7!%R#I?cG-7C^hcH)@l?WC1vyw$FFu_(r)jhOq6p}W8sG7NO{YTy8tG4 zrb$tTkag*G?(7lfoGx$4YWui>{{@}-FB2ub=}RX{1zx?j)s-##J9|G7E1@-;7Nuln z9MQoX7FJ76+D#XXT@ZZmLZCufIdf3@OigG6m8I7!GT=7VD|>?6e!z9=eT}*E_tSn6 zl+clHCZ-kcIR#gen#LjMJW8>0QtViaQB#FhqsCb0YPYr3;jRITl@V9Aph24D?r2d` zetCyyCg<*O-u+M& zW^ptmT|}p$VAOZpmbQ1{5fK-6ytEvre#Po}6c2URn`viQAF2+e?Z~PK2&pd>7=7)I zTCYm)@3PFRu_6a6Kb)IpCzQ%e3l%O#SDA+$Pq{Dk{HCqi7z>qd{nVpebffL7h{c4( zmhXn~G+C27S3(IfC)q2KON=YwqHXEo%zc40DgWLzF{%RIdr@RcLu90qMSHf!Y}JaqP<={8_Rfe;ddR5= zKEo;^Yip&^m((#{czE{kUga3-@`*;&EwO}Jt>QdURP2P>ob^j-A!qld-0S_pm)kjs zkNo48oZnMt){W~o8g^f;4#?lRLr-T@f}wH1o~-Iq=NEVtTVEZ`vrW~!>2yh%;Bc~H zHl&OK>n@d`*e19*9#v>zZpU?I);f7}IPIfSSk#N|ujE492Itg)l!)TJ19@FE^x|p= zH16NC7OfK&|6_!AnWfTIf^YPOa&`|nbk3VR0vql6&s@y1V3QOU%(`Re+kJgrz?r9!{^wOQ4W-eng23gc}f(LxIs zH_Ls~5izbjcRQH#WH6s6hR;zn>j_R8aJ$A)6xNneu8UI-vWV8Z@HZu&WwvG5q{1ZS zdZeVf{Pv5-u281~y;aJe*x%Uv0@biMZ$vPbKj}O`(SOWQc~kJX` zXR&d4DtAe@2RH$^ z0os5*;0eIUeJi3Uh`A%44x(XzjClG8BO~-r_A}odiRuHo2-86#`mhrgN5p~<$RLY? zq(kynfFA5{v#p+EA1 z5aoe1763EQHorRm`C&ktKn(OQ1n)$Q{GZz&jRb`eDEMpl<0O#+)DMV(T7nsIzCG{QuM->B9g7Lrl2SE&gW`M!~(un|y0fIn=b^6_$ z9{zEzgYI~39xn0ZP*9qBL%fg7rg$ttt&TOmvfNNO<6FT0ZavM$Y4CYLQGIcIYv9Y& zBGPUh&QTfW;V2!)oIra@s&d968y-y}Y|ww(R$GzWS*V&)k@W0>Slem{|HdTCjm;_5 zwY*A8W3nUbemE^_f0ng$tbd<`sr?TO-_&VCw+F#7P@LkIl$1PzTBoPY1b88EIO>UO zP-NK7+g2yD3U6g3i|iA6+su>54sf_Sk0F=)1|9odnCM4u2Rs z=&Y?-V&VquSN%3FJ2~ZGweP~iLs|w=l@9yu$tj@}Dp?e-2JUsqOoswdXb=E%&0te_ zA2M+{5Hf-dqD7=yw*r@A*xkn(1IS~nfP}k}e?4Bt|9g(eph4hFX_|S6nj1&Sz9z^= zRw~<&-9d@FzTn6S*RVE{Wj5lgLJr9HLB8S9CgOm*>XA8*y4`JE;^s$=bqD#U4;e5C&x&ggKIAVL zrQ)Yd8|{>7Z(6*B&7&4&9(*vDOfHMuR-Dk1IZia*XM^EZUD^{?cWG>J>KrtElc*{K zaVl(7SN2cH4I6Q$bZOpJ8e5LKaG7p;?tJ~#+9QrTYU@f#5`Vo7cEX!szCT}iX-K^2 w#3o+=C+lQz2J+SOEzVX(eJ)e7=eicC{rr9U2VGDcdH?_b literal 0 HcmV?d00001 diff --git a/test/AutobahnTestApp/TestResources/testCert.txt b/test/AutobahnTestApp/TestResources/testCert.txt new file mode 100644 index 0000000000..8771b78318 --- /dev/null +++ b/test/AutobahnTestApp/TestResources/testCert.txt @@ -0,0 +1,3 @@ +The password for this is 'testPassword' + +DO NOT EVER TRUST THIS CERT. The private key for it is publicly released. \ No newline at end of file diff --git a/test/AutobahnTestApp/project.json b/test/AutobahnTestApp/project.json new file mode 100644 index 0000000000..fc3aab9d44 --- /dev/null +++ b/test/AutobahnTestApp/project.json @@ -0,0 +1,55 @@ +{ + "dependencies": { + "Microsoft.AspNetCore.WebSockets.Server": { "target": "project" }, + "Microsoft.NETCore.App": { + "version": "1.0.0", + "type": "platform" + }, + + "Microsoft.AspNetCore.Diagnostics": "1.1.0-*", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-*", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0-*", + "Microsoft.AspNetCore.Server.WebListener": "0.2.0-*", + "Microsoft.AspNetCore.Server.Kestrel.Https": "1.1.0-*", + "Microsoft.Extensions.Configuration.CommandLine": "1.1.0-*", + "Microsoft.Extensions.Logging.Console": "1.1.0-*" + }, + + "tools": { + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dotnet5.6", + "portable-net45+win8" + ] + } + }, + + "buildOptions": { + "emitEntryPoint": true, + "copyToOutput": [ + "TestResources/testCert.pfx" + ] + }, + + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + }, + + "publishOptions": { + "include": [ + "wwwroot", + "web.config", + "TestResources/testCert.pfx" + ] + }, + + "scripts": { + "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] + } +} diff --git a/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 b/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 new file mode 100644 index 0000000000..52f35e492f --- /dev/null +++ b/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 @@ -0,0 +1,30 @@ +# +# RunAutobahnTests.ps1 +# +param([Parameter(Mandatory=$true)][string]$ServerUrl, [string[]]$Cases = @("*"), [string]$OutputDir) + +if(!(Get-Command wstest -ErrorAction SilentlyContinue)) { + throw "Missing required command 'wstest'. See README.md in Microsoft.AspNetCore.WebSockets.Server.Test project for information on installing Autobahn Test Suite." +} + +if(!$OutputDir) { + $OutputDir = Convert-Path "." + $OutputDir = Join-Path $OutputDir "autobahnreports" +} + +$Spec = Convert-Path (Join-Path $PSScriptRoot "autobahn.spec.json") + +$CasesArray = [string]::Join(",", @($Cases | ForEach-Object { "`"$_`"" })) + +$SpecJson = [IO.File]::ReadAllText($Spec).Replace("OUTPUTDIR", $OutputDir.Replace("\", "\\")).Replace("WEBSOCKETURL", $ServerUrl).Replace("`"CASES`"", $CasesArray) + +$TempFile = [IO.Path]::GetTempFileName() + +try { + [IO.File]::WriteAllText($TempFile, $SpecJson) + & wstest -m fuzzingclient -s $TempFile +} finally { + if(Test-Path $TempFile) { + rm $TempFile + } +} \ No newline at end of file diff --git a/test/AutobahnTestApp/scripts/autobahn.spec.json b/test/AutobahnTestApp/scripts/autobahn.spec.json new file mode 100644 index 0000000000..aa6d841167 --- /dev/null +++ b/test/AutobahnTestApp/scripts/autobahn.spec.json @@ -0,0 +1,14 @@ +{ + "options": { "failByDrop": false }, + "outdir": "OUTPUTDIR", + "servers": [ + { + "agent": "Server", + "url": "WEBSOCKETURL", + "options": { "version": 18 } + } + ], + "cases": ["CASES"], + "exclude-cases": ["12.*", "13.*"], + "exclude-agent-cases": {} +} diff --git a/test/AutobahnTestServer/web.config b/test/AutobahnTestApp/web.config similarity index 57% rename from test/AutobahnTestServer/web.config rename to test/AutobahnTestApp/web.config index 41b492941a..dc0514fca5 100644 --- a/test/AutobahnTestServer/web.config +++ b/test/AutobahnTestApp/web.config @@ -1,9 +1,14 @@ - + + + + - + - \ No newline at end of file + diff --git a/test/AutobahnTestClient/Program.cs b/test/AutobahnTestClient/Program.cs deleted file mode 100644 index 6a3ae749a7..0000000000 --- a/test/AutobahnTestClient/Program.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.WebSockets.Client; - -namespace AutobahnTestClient -{ - public class Program - { - public static void Main(string[] args) - { - new Program().Run(args).Wait(); - } - - private async Task Run(string[] args) - { - try - { - string serverAddress = "ws://localhost:5000"; - string agent = - "ManagedWebSockets"; - // "NativeWebSockets"; - - Console.WriteLine("Getting case count."); - var caseCount = await GetCaseCountAsync(serverAddress, agent); - Console.WriteLine(caseCount + " case(s)."); - - for (int i = 1; i <= caseCount; i++) - { - await RunCaseAsync(serverAddress, i, agent); - } - - await UpdateReportsAsync(serverAddress, agent); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - - Console.WriteLine("Done"); - Console.ReadLine(); - } - - private async Task ConnectAsync(string address, string agent) - { - if (string.Equals(agent, "NativeWebSockets")) - { - var client = new ClientWebSocket(); - await client.ConnectAsync(new Uri(address), CancellationToken.None); - return client; - } - else - { - // TODO: BUG: Require ws or wss schemes - var client = new WebSocketClient(); - return await client.ConnectAsync(new Uri(address), CancellationToken.None); - } - } - - private async Task GetCaseCountAsync(string serverAddress, string agent) - { - var webSocket = await ConnectAsync(serverAddress + "/getCaseCount", agent); - byte[] buffer = new byte[1024 * 4]; - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - var caseCountText = Encoding.UTF8.GetString(buffer, 0, result.Count); - return int.Parse(caseCountText); - } - - private async Task RunCaseAsync(string serverAddress, int caseId, string agent) - { - try - { - Console.WriteLine("Running case " + caseId); - var webSocket = await ConnectAsync(serverAddress + "/runCase?case=" + caseId + "&agent=" + agent, agent); - await Echo(webSocket); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - private async Task Echo(WebSocket webSocket) - { - byte[] buffer = new byte[1024 * 4]; - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - - private async Task UpdateReportsAsync(string serverAddress, string agent) - { - var webSocket = await ConnectAsync(serverAddress + "/updateReports?agent=" + agent, agent); - await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - } - } -} diff --git a/test/AutobahnTestClient/ReadMe.txt b/test/AutobahnTestClient/ReadMe.txt deleted file mode 100644 index 6da6360ccc..0000000000 --- a/test/AutobahnTestClient/ReadMe.txt +++ /dev/null @@ -1,23 +0,0 @@ -This test server is for use in testing client side implementations of the WebSocekt protocol. It is currently implemented to test -Microsoft.AspNetCore.WebSockets.Client.WebSocketClient and System.Net.WebSockets.ClientWebSocket. - -See http://autobahn.ws/ to download and install the test framework. - -Usage: -Run the test server: -"C:\Program Files\Python\2.7.6\Scripts\wstest" -d -m fuzzingserver -s fuzzingserver.json -Where fuzzingserver.json contains the following: - -{ - "url": "ws://127.0.0.1:9001", - - "options": {"failByDrop": false}, - "outdir": "./reports/clients", - "webport": 8080, - - "cases": ["*"], - "exclude-cases": [], - "exclude-agent-cases": {} -} - -Then run the client of your choice, taking care to update the serverAddress and agent fields in the client code. \ No newline at end of file diff --git a/test/AutobahnTestClient/project.json b/test/AutobahnTestClient/project.json deleted file mode 100644 index b900bced6b..0000000000 --- a/test/AutobahnTestClient/project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "buildOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "Microsoft.AspNetCore.WebSockets.Client": "0.2.0-*" - }, - "frameworks": { - "net451": {} - } -} \ No newline at end of file diff --git a/test/AutobahnTestServer/ReadMe.txt b/test/AutobahnTestServer/ReadMe.txt deleted file mode 100644 index 0c0bb6837d..0000000000 --- a/test/AutobahnTestServer/ReadMe.txt +++ /dev/null @@ -1,25 +0,0 @@ -This test server is for use in testing server side implementations of the WebSocekt protocol. It should work with Helios, WebListener (native), -and the managed implementation of WebSockets in this project (running on WebListener, Kestrel, etc.). The tests only require that the server implement -a basic WebSocket accept and then echo any content received. - -See http://autobahn.ws/ to download and install the test framework. - -Usage: -Configure and start the server of your choice. -Run the test client: -"C:\Program Files\Python\2.7.6\Scripts\wstest.exe" -d -m fuzzingclient -s fuzzingclient.json -Where fussingclient.json contains: -{ - "options": {"failByDrop": false}, - "outdir": "./reports/servers", - - "servers": [ - {"agent": "NameOfImplementationBeingTested", - "url": "ws://localhost:12345", - "options": {"version": 18}} - ], - - "cases": ["*"], - "exclude-cases": [], - "exclude-agent-cases": {} -} \ No newline at end of file diff --git a/test/AutobahnTestServer/Startup.cs b/test/AutobahnTestServer/Startup.cs deleted file mode 100644 index 7330152214..0000000000 --- a/test/AutobahnTestServer/Startup.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; - -namespace AutobahnTestServer -{ - public class Startup - { - public void Configure(IApplicationBuilder app) - { - app.Map("/Managed", managedWebSocketsApp => - { - // Comment this out to test native server implementations - managedWebSocketsApp.UseWebSockets(new WebSocketOptions - { - ReplaceFeature = true - }); - - managedWebSocketsApp.Use(async (context, next) => - { - if (context.WebSockets.IsWebSocketRequest) - { - Console.WriteLine("Echo: " + context.Request.Path); - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - return; - } - await next(); - }); - }); - - app.Map("/Native", nativeWebSocketsApp => - { - nativeWebSocketsApp.Use(async (context, next) => - { - if (context.WebSockets.IsWebSocketRequest) - { - Console.WriteLine("Echo: " + context.Request.Path); - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - return; - } - await next(); - }); - }); - - app.Run(context => - { - Console.WriteLine("Hello World"); - return context.Response.WriteAsync("Hello World"); - }); - } - - private async Task Echo(WebSocket webSocket) - { - byte[] buffer = new byte[1024 * 4]; - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - - public static void Main(string[] args) - { - var host = new WebHostBuilder() - .UseKestrel() - .UseIISIntegration() - .UseStartup() - .Build(); - - host.Run(); - } - } -} diff --git a/test/AutobahnTestServer/project.json b/test/AutobahnTestServer/project.json deleted file mode 100644 index 997723dc70..0000000000 --- a/test/AutobahnTestServer/project.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "dependencies": { - "Microsoft.AspNetCore.WebSockets.Server": "0.2.0-*", - "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-*", - "Microsoft.AspNetCore.Server.Kestrel": "1.1.0-*" - }, - "buildOptions": { - "emitEntryPoint": true - }, - "frameworks": { - "netcoreapp1.0": { - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0-*", - "type": "platform" - }, - "dotnet-test-xunit": "2.2.0-*" - } - }, - "net451": {} - }, - "publishOptions": { - "include": [ - "web.config" - ] - }, - "tools": { - "Microsoft.AspNetCore.Server.IISIntegration.Tools": { - "version": "1.0.0-*", - "imports": "portable-net45+wp80+win8+wpa81+dnxcore50" - } - }, - "scripts": { - "postpublish": "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Client.Test/Microsoft.AspNetCore.WebSockets.Client.Test.xproj b/test/Microsoft.AspNetCore.WebSockets.Client.Test/Microsoft.AspNetCore.WebSockets.Client.Test.xproj index e8c407ffd3..e54463273b 100644 --- a/test/Microsoft.AspNetCore.WebSockets.Client.Test/Microsoft.AspNetCore.WebSockets.Client.Test.xproj +++ b/test/Microsoft.AspNetCore.WebSockets.Client.Test/Microsoft.AspNetCore.WebSockets.Client.Test.xproj @@ -13,5 +13,8 @@ 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Protocol.Test/Microsoft.AspNetCore.WebSockets.Protocol.Test.xproj b/test/Microsoft.AspNetCore.WebSockets.Protocol.Test/Microsoft.AspNetCore.WebSockets.Protocol.Test.xproj index 1025dc3503..80b9e72b45 100644 --- a/test/Microsoft.AspNetCore.WebSockets.Protocol.Test/Microsoft.AspNetCore.WebSockets.Protocol.Test.xproj +++ b/test/Microsoft.AspNetCore.WebSockets.Protocol.Test/Microsoft.AspNetCore.WebSockets.Protocol.Test.xproj @@ -13,5 +13,8 @@ 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnCaseResult.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnCaseResult.cs new file mode 100644 index 0000000000..017d178880 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnCaseResult.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnCaseResult + { + public string Name { get; } + public string ActualBehavior { get; } + + public AutobahnCaseResult(string name, string actualBehavior) + { + Name = name; + ActualBehavior = actualBehavior; + } + + public static AutobahnCaseResult FromJson(JProperty prop) + { + var caseObj = (JObject)prop.Value; + var actualBehavior = (string)caseObj["behavior"]; + return new AutobahnCaseResult(prop.Name, actualBehavior); + } + + public bool BehaviorIs(params string[] behaviors) + { + return behaviors.Any(b => string.Equals(b, ActualBehavior, StringComparison.Ordinal)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnExpectations.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnExpectations.cs new file mode 100644 index 0000000000..dfcf9a4c25 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnExpectations.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Server.Testing; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnExpectations + { + private Dictionary _expectations = new Dictionary(); + public bool Ssl { get; } + public ServerType Server { get; } + public string Environment { get; } + + public AutobahnExpectations(ServerType server, bool ssl, string environment) + { + Server = server; + Ssl = ssl; + Environment = environment; + } + + public AutobahnExpectations Fail(params string[] caseSpecs) => Expect(Expectation.Fail, caseSpecs); + public AutobahnExpectations NonStrict(params string[] caseSpecs) => Expect(Expectation.NonStrict, caseSpecs); + public AutobahnExpectations OkOrNonStrict(params string[] caseSpecs) => Expect(Expectation.OkOrNonStrict, caseSpecs); + public AutobahnExpectations OkOrFail(params string[] caseSpecs) => Expect(Expectation.OkOrFail, caseSpecs); + + public AutobahnExpectations Expect(Expectation expectation, params string[] caseSpecs) + { + foreach (var caseSpec in caseSpecs) + { + _expectations[caseSpec] = expectation; + } + return this; + } + + internal void Verify(AutobahnServerResult serverResult, StringBuilder failures) + { + foreach (var caseResult in serverResult.Cases) + { + // If this is an informational test result, we can't compare it to anything + if (!string.Equals(caseResult.ActualBehavior, "INFORMATIONAL", StringComparison.Ordinal)) + { + Expectation expectation; + if (!_expectations.TryGetValue(caseResult.Name, out expectation)) + { + expectation = Expectation.Ok; + } + + switch (expectation) + { + case Expectation.Fail: + if (!caseResult.BehaviorIs("FAILED")) + { + failures.AppendLine($"Case {serverResult.Name}:{caseResult.Name}. Expected 'FAILED', but got '{caseResult.ActualBehavior}'"); + } + break; + case Expectation.NonStrict: + if (!caseResult.BehaviorIs("NON-STRICT")) + { + failures.AppendLine($"Case {serverResult.Name}:{caseResult.Name}. Expected 'NON-STRICT', but got '{caseResult.ActualBehavior}'"); + } + break; + case Expectation.Ok: + if (!caseResult.BehaviorIs("OK")) + { + failures.AppendLine($"Case {serverResult.Name}:{caseResult.Name}. Expected 'OK', but got '{caseResult.ActualBehavior}'"); + } + break; + case Expectation.OkOrNonStrict: + if (!caseResult.BehaviorIs("NON-STRICT") && !caseResult.BehaviorIs("OK")) + { + failures.AppendLine($"Case {serverResult.Name}:{caseResult.Name}. Expected 'NON-STRICT' or 'OK', but got '{caseResult.ActualBehavior}'"); + } + break; + case Expectation.OkOrFail: + if (!caseResult.BehaviorIs("FAILED") && !caseResult.BehaviorIs("OK")) + { + failures.AppendLine($"Case {serverResult.Name}:{caseResult.Name}. Expected 'FAILED' or 'OK', but got '{caseResult.ActualBehavior}'"); + } + break; + default: + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnResult.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnResult.cs new file mode 100644 index 0000000000..c23164a13c --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnResult.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Server.Testing; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnResult + { + public IEnumerable Servers { get; } + + public AutobahnResult(IEnumerable servers) + { + Servers = servers; + } + + public static AutobahnResult FromReportJson(JObject indexJson) + { + // Load the report + return new AutobahnResult(indexJson.Properties().Select(AutobahnServerResult.FromJson)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnServerResult.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnServerResult.cs new file mode 100644 index 0000000000..4986bbdfc6 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnServerResult.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Server.Testing; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnServerResult + { + public ServerType Server { get; } + public bool Ssl { get; } + public string Environment { get; } + public string Name { get; } + public IEnumerable Cases { get; } + + public AutobahnServerResult(string name, IEnumerable cases) + { + Name = name; + + var splat = name.Split('|'); + if (splat.Length < 3) + { + throw new FormatException("Results incorrectly formatted"); + } + + Server = (ServerType)Enum.Parse(typeof(ServerType), splat[0]); + Ssl = string.Equals(splat[1], "SSL", StringComparison.Ordinal); + Environment = splat[2]; + Cases = cases; + } + + public static AutobahnServerResult FromJson(JProperty prop) + { + var valueObj = ((JObject)prop.Value); + return new AutobahnServerResult(prop.Name, valueObj.Properties().Select(AutobahnCaseResult.FromJson)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnSpec.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnSpec.cs new file mode 100644 index 0000000000..0c97999136 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnSpec.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnSpec + { + public string OutputDirectory { get; } + public IList Servers { get; } = new List(); + public IList Cases { get; } = new List(); + public IList ExcludedCases { get; } = new List(); + + public AutobahnSpec(string outputDirectory) + { + OutputDirectory = outputDirectory; + } + + public AutobahnSpec WithServer(string name, string url) + { + Servers.Add(new ServerSpec(name, url)); + return this; + } + + public AutobahnSpec IncludeCase(params string[] caseSpecs) + { + foreach (var caseSpec in caseSpecs) + { + Cases.Add(caseSpec); + } + return this; + } + + public AutobahnSpec ExcludeCase(params string[] caseSpecs) + { + foreach (var caseSpec in caseSpecs) + { + ExcludedCases.Add(caseSpec); + } + return this; + } + + public void WriteJson(string file) + { + File.WriteAllText(file, GetJson().ToString(Formatting.Indented)); + } + + public JObject GetJson() => new JObject( + new JProperty("options", new JObject( + new JProperty("failByDrop", false))), + new JProperty("outdir", OutputDirectory), + new JProperty("servers", new JArray(Servers.Select(s => s.GetJson()).ToArray())), + new JProperty("cases", new JArray(Cases.ToArray())), + new JProperty("exclude-cases", new JArray(ExcludedCases.ToArray())), + new JProperty("exclude-agent-cases", new JObject())); + } +} diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnTester.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnTester.cs new file mode 100644 index 0000000000..ed74e67b5a --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/AutobahnTester.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.Testing; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class AutobahnTester : IDisposable + { + private int _nextPort; + private readonly List _deployers = new List(); + private readonly List _expectations = new List(); + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; + + public AutobahnSpec Spec { get; } + + public AutobahnTester(ILoggerFactory loggerFactory, AutobahnSpec baseSpec) : this(7000, loggerFactory, baseSpec) { } + + public AutobahnTester(int startPort, ILoggerFactory loggerFactory, AutobahnSpec baseSpec) + { + _nextPort = startPort; + _loggerFactory = loggerFactory; + _logger = _loggerFactory.CreateLogger("AutobahnTester"); + + Spec = baseSpec; + } + + public async Task Run() + { + var specFile = Path.GetTempFileName(); + try + { + Spec.WriteJson(specFile); + + // Run the test (write something to the console so people know this will take a while...) + _logger.LogInformation("Now launching Autobahn Test Suite. This will take a while."); + var exitCode = await Wstest.Default.ExecAsync("-m fuzzingclient -s " + specFile); + if (exitCode != 0) + { + throw new Exception("wstest failed"); + } + } + finally + { + if (File.Exists(specFile)) + { + File.Delete(specFile); + } + } + + // Parse the output. + var outputFile = Path.Combine(Directory.GetCurrentDirectory(), Spec.OutputDirectory, "index.json"); + using (var reader = new StreamReader(File.OpenRead(outputFile))) + { + return AutobahnResult.FromReportJson(JObject.Parse(await reader.ReadToEndAsync())); + } + } + + public void Verify(AutobahnResult result) + { + var failures = new StringBuilder(); + foreach (var serverResult in result.Servers) + { + var serverExpectation = _expectations.FirstOrDefault(e => e.Server == serverResult.Server && e.Ssl == serverResult.Ssl); + if (serverExpectation == null) + { + failures.AppendLine($"Expected no results for server: {serverResult.Name} but found results!"); + } + else + { + serverExpectation.Verify(serverResult, failures); + } + } + + Assert.True(failures.Length == 0, "Autobahn results did not meet expectations:" + Environment.NewLine + failures.ToString()); + } + + public async Task DeployTestAndAddToSpec(ServerType server, bool ssl, string environment, Action expectationConfig = null) + { + var port = Interlocked.Increment(ref _nextPort); + var baseUrl = ssl ? $"https://localhost:{port}" : $"http://localhost:{port}"; + var sslNamePart = ssl ? "SSL" : "NoSSL"; + var name = $"{server}|{sslNamePart}|{environment}"; + var logger = _loggerFactory.CreateLogger($"AutobahnTestApp:{server}:{sslNamePart}:{environment}"); + + var appPath = Helpers.GetApplicationPath("AutobahnTestApp"); + var parameters = new DeploymentParameters(appPath, server, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64) + { + ApplicationBaseUriHint = baseUrl, + ApplicationType = ApplicationType.Portable, + TargetFramework = "netcoreapp1.0", + EnvironmentName = environment + }; + + var deployer = ApplicationDeployerFactory.Create(parameters, logger); + var result = deployer.Deploy(); + +#if NET451 + System.Net.ServicePointManager.ServerCertificateValidationCallback = (_, __, ___, ____) => true; + var client = new HttpClient(); +#else + var handler = new HttpClientHandler(); + if (ssl) + { + // Don't take this out of the "if(ssl)". If we set it on some platforms, it crashes + // So we avoid running SSL tests on those platforms (for now). + // See https://github.com/dotnet/corefx/issues/9728 + handler.ServerCertificateCustomValidationCallback = (_, __, ___, ____) => true; + } + var client = new HttpClient(handler); +#endif + + // Make sure the server works + var resp = await RetryHelper.RetryRequest(() => + { + return client.GetAsync(result.ApplicationBaseUri); + }, logger, result.HostShutdownToken, retryCount: 5); + resp.EnsureSuccessStatusCode(); + + // Add to the current spec + var wsUrl = result.ApplicationBaseUri.Replace("https://", "wss://").Replace("http://", "ws://"); + Spec.WithServer(name, wsUrl); + + _deployers.Add(deployer); + + var expectations = new AutobahnExpectations(server, ssl, environment); + expectationConfig?.Invoke(expectations); + _expectations.Add(expectations); + } + + public void Dispose() + { + foreach (var deployer in _deployers) + { + deployer.Dispose(); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Executable.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Executable.cs new file mode 100644 index 0000000000..eb9a46a889 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Executable.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class Executable + { + private static readonly string _exeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty; + + private readonly string _path; + + protected Executable(string path) + { + _path = path; + } + + public static string Locate(string name) + { + foreach (var dir in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator)) + { + var candidate = Path.Combine(dir, name + _exeSuffix); + if (File.Exists(candidate)) + { + return candidate; + } + } + return null; + } + + public Task ExecAsync(string args) + { + var process = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = _path, + Arguments = args, + UseShellExecute = false, + }, + EnableRaisingEvents = true + }; + var tcs = new TaskCompletionSource(); + + process.Exited += (_, __) => tcs.TrySetResult(process.ExitCode); + + process.Start(); + + return tcs.Task; + } + } +} diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Expectation.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Expectation.cs new file mode 100644 index 0000000000..a3a8040f9d --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Expectation.cs @@ -0,0 +1,11 @@ +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public enum Expectation + { + Fail, + NonStrict, + OkOrFail, + Ok, + OkOrNonStrict + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/ServerSpec.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/ServerSpec.cs new file mode 100644 index 0000000000..265d1f7971 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/ServerSpec.cs @@ -0,0 +1,23 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + public class ServerSpec + { + public string Name { get; } + public string Url { get; } + + public ServerSpec(string name, string url) + { + Name = name; + Url = url; + } + + public JObject GetJson() => new JObject( + new JProperty("agent", Name), + new JProperty("url", Url), + new JProperty("options", new JObject( + new JProperty("version", 18)))); + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Wstest.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Wstest.cs new file mode 100644 index 0000000000..07d09d34f5 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Autobahn/Wstest.cs @@ -0,0 +1,22 @@ +using System; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn +{ + /// + /// Wrapper around the Autobahn Test Suite's "wstest" app. + /// + public class Wstest : Executable + { + private static Lazy _instance = new Lazy(Create); + + public static Wstest Default => _instance.Value; + + public Wstest(string path) : base(path) { } + + private static Wstest Create() + { + var location = Locate("wstest"); + return location == null ? null : new Wstest(location); + } + } +} diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/AutobahnTests.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/AutobahnTests.cs new file mode 100644 index 0000000000..c2c685af21 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/AutobahnTests.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.Testing; +using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.PlatformAbstractions; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test +{ + public class AutobahnTests + { + // Skip if wstest is not installed for now, see https://github.com/aspnet/WebSockets/issues/95 + // We will enable Wstest on every build once we've gotten the necessary infrastructure sorted out :). + [ConditionalFact] + [SkipIfWsTestNotPresent] + public async Task AutobahnTestSuite() + { + var reportDir = Environment.GetEnvironmentVariable("AUTOBAHN_SUITES_REPORT_DIR"); + var outDir = !string.IsNullOrEmpty(reportDir) ? + reportDir : + Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "autobahnreports"); + + if (Directory.Exists(outDir)) + { + Directory.Delete(outDir, recursive: true); + } + + outDir = outDir.Replace("\\", "\\\\"); + + // 9.* is Limits/Performance which is VERY SLOW; 12.*/13.* are compression which we don't implement + var spec = new AutobahnSpec(outDir) + .IncludeCase("*") + .ExcludeCase("9.*", "12.*", "13.*"); + + var loggerFactory = new LoggerFactory(); // No logging! It's very loud... + + AutobahnResult result; + using (var tester = new AutobahnTester(loggerFactory, spec)) + { + await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: false, environment: "ManagedSockets", expectationConfig: expect => expect + .NonStrict("6.4.3", "6.4.4")); // https://github.com/aspnet/WebSockets/issues/99 + + // Windows-only IIS tests, and Kestrel SSL tests (due to: https://github.com/aspnet/WebSockets/issues/102) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: true, environment: "ManagedSockets", expectationConfig: expect => expect + .NonStrict("6.4.3", "6.4.4")); // https://github.com/aspnet/WebSockets/issues/99 + + if (IsIISExpress10Installed()) + { + // IIS Express tests are a bit flaky, some tests fail occasionally or get non-strict passes + // https://github.com/aspnet/WebSockets/issues/100 + await tester.DeployTestAndAddToSpec(ServerType.IISExpress, ssl: false, environment: "ManagedSockets", expectationConfig: expect => expect + .OkOrFail(Enumerable.Range(1, 20).Select(i => $"5.{i}").ToArray()) // 5.* occasionally fail on IIS express + .OkOrNonStrict("3.2", "3.3", "3.4", "4.1.3", "4.1.4", "4.1.5", "4.2.3", "4.2.4", "4.2.5", "5.15")); // These occasionally get non-strict results + } + + if (IsWindows8OrHigher()) + { + await tester.DeployTestAndAddToSpec(ServerType.WebListener, ssl: false, environment: "ManagedSockets", expectationConfig: expect => expect + .Fail("6.1.2", "6.1.3") // https://github.com/aspnet/WebSockets/issues/97 + .NonStrict("6.4.3", "6.4.4")); // https://github.com/aspnet/WebSockets/issues/99 + } + } + + // REQUIRES a build of WebListener that supports native WebSockets, which we don't have right now + //await tester.DeployTestAndAddToSpec(ServerType.WebListener, ssl: false, environment: "NativeSockets"); + + result = await tester.Run(); + tester.Verify(result); + } + } + + private bool IsWindows8OrHigher() + { + const string WindowsName = "Microsoft Windows "; + const int VersionOffset = 18; + + if (RuntimeInformation.OSDescription.StartsWith(WindowsName)) + { + var versionStr = RuntimeInformation.OSDescription.Substring(VersionOffset); + Version version; + if (Version.TryParse(versionStr, out version)) + { + return version.Major > 6 || (version.Major == 6 && version.Minor >= 2); + } + } + + return false; + } + + private bool IsIISExpress10Installed() + { + var pf = Environment.GetEnvironmentVariable("PROGRAMFILES"); + var iisExpressExe = Path.Combine(pf, "IIS Express", "iisexpress.exe"); + return File.Exists(iisExpressExe) && FileVersionInfo.GetVersionInfo(iisExpressExe).FileMajorPart >= 10; + } + } +} diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Helpers.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Helpers.cs new file mode 100644 index 0000000000..a22eb25bf3 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Helpers.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using Microsoft.Extensions.PlatformAbstractions; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test +{ + public class Helpers + { + public static string GetApplicationPath(string projectName) + { + var applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; + + var directoryInfo = new DirectoryInfo(applicationBasePath); + do + { + var solutionFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "WebSockets.sln")); + if (solutionFileInfo.Exists) + { + return Path.GetFullPath(Path.Combine(directoryInfo.FullName, "test", projectName)); + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new Exception($"Solution root could not be found using {applicationBasePath}"); + } + } +} diff --git a/test/AutobahnTestClient/AutobahnTestClient.xproj b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Microsoft.AspNetCore.WebSockets.Server.Test.xproj similarity index 66% rename from test/AutobahnTestClient/AutobahnTestClient.xproj rename to test/Microsoft.AspNetCore.WebSockets.Server.Test/Microsoft.AspNetCore.WebSockets.Server.Test.xproj index ef9c52ad9d..11f55fd48c 100644 --- a/test/AutobahnTestClient/AutobahnTestClient.xproj +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Microsoft.AspNetCore.WebSockets.Server.Test.xproj @@ -4,14 +4,17 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + - bc4d2bb1-05a8-4816-8bc1-3a664f09ee32 + e82d9f64-8afa-4dcb-a842-2283fda73be8 .\obj .\bin\ 2.0 - + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/Properties/AssemblyInfo.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6e03af7061 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +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: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.AspNetCore.WebSockets.Server.Test")] +[assembly: AssemblyTrademark("")] + +// 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("e82d9f64-8afa-4dcb-a842-2283fda73be8")] diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/SkipIfWsTestNotPresentAttribute.cs b/test/Microsoft.AspNetCore.WebSockets.Server.Test/SkipIfWsTestNotPresentAttribute.cs new file mode 100644 index 0000000000..b37fd95868 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/SkipIfWsTestNotPresentAttribute.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.WebSockets.Server.Test.Autobahn; + +namespace Microsoft.AspNetCore.WebSockets.Server.Test +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class SkipIfWsTestNotPresentAttribute : Attribute, ITestCondition + { + public bool IsMet => Wstest.Default != null; + public string SkipReason => "Autobahn Test Suite is not installed on the host machine."; + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.WebSockets.Server.Test/project.json b/test/Microsoft.AspNetCore.WebSockets.Server.Test/project.json new file mode 100644 index 0000000000..9054cd9eb8 --- /dev/null +++ b/test/Microsoft.AspNetCore.WebSockets.Server.Test/project.json @@ -0,0 +1,23 @@ +{ + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Server.Testing": "0.2.0-*", + "Microsoft.AspNetCore.Testing": "1.1.0-*", + "Microsoft.Extensions.Logging": "1.1.0-*", + "Microsoft.Extensions.Logging.Console": "1.1.0-*", + "Microsoft.Extensions.PlatformAbstractions": "1.1.0-*", + "System.Diagnostics.FileVersionInfo": "4.0.0", + "xunit": "2.2.0-*" + }, + "testRunner": "xunit", + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0-*", + "type": "platform" + } + } + } + } +}