From 999ae15266098f91226f47e536ee6dbdc86e3864 Mon Sep 17 00:00:00 2001 From: Frederik Carlier <frederik.carlier@quamotion.mobi> Date: Wed, 3 Aug 2016 18:28:23 +0200 Subject: [PATCH] Use a custom contract resolver in the CommandResposeFactory, to ensure that properties with private setters have their values set, too. --- .../ChromeDevTools/CommandResponseFactory.cs | 18 +++- .../Serialization/MessageContractResolver.cs | 25 ++++- .../CommandResponseFactoryTests.cs | 32 +++++++ .../MasterDevs.ChromeDevTools.Tests.csproj | 94 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++++ .../response-1.json | 61 ++++++++++++ source/MasterDevs.ChromeDevTools.sln | 8 +- 7 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 source/MasterDevs.ChromeDevTools.Tests/CommandResponseFactoryTests.cs create mode 100644 source/MasterDevs.ChromeDevTools.Tests/MasterDevs.ChromeDevTools.Tests.csproj create mode 100644 source/MasterDevs.ChromeDevTools.Tests/Properties/AssemblyInfo.cs create mode 100644 source/MasterDevs.ChromeDevTools.Tests/response-1.json diff --git a/source/ChromeDevTools/CommandResponseFactory.cs b/source/ChromeDevTools/CommandResponseFactory.cs index 562daca..6e220e5 100644 --- a/source/ChromeDevTools/CommandResponseFactory.cs +++ b/source/ChromeDevTools/CommandResponseFactory.cs @@ -1,4 +1,7 @@ -using Newtonsoft.Json.Linq; +using MasterDevs.ChromeDevTools.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using System; namespace MasterDevs.ChromeDevTools @@ -8,10 +11,21 @@ namespace MasterDevs.ChromeDevTools private readonly IMethodTypeMap _methodTypeMap; private readonly ICommandFactory _commandFactory; + /// <summary> + /// Used to deserialize command responses from JSON to .NET objects. + /// </summary> + private readonly JsonSerializer _serializer; + public CommandResponseFactory(IMethodTypeMap methodTypeMap, ICommandFactory commandFactory) + : this(methodTypeMap, commandFactory, new JsonSerializer() { ContractResolver = new MessageContractResolver() }) + { + } + + public CommandResponseFactory(IMethodTypeMap methodTypeMap, ICommandFactory commandFactory, JsonSerializer serializer) { _methodTypeMap = methodTypeMap; _commandFactory = commandFactory; + _serializer = serializer; } public ICommandResponse Create(byte[] responseBytes) @@ -38,7 +52,7 @@ namespace MasterDevs.ChromeDevTools } var genericEventType = typeof(CommandResponse<>); var commandResponseType = genericEventType.MakeGenericType(typeInferredFromMethod); - var result = jObject.ToObject(commandResponseType); + var result = jObject.ToObject(commandResponseType, _serializer); return result as ICommandResponse; } diff --git a/source/ChromeDevTools/Serialization/MessageContractResolver.cs b/source/ChromeDevTools/Serialization/MessageContractResolver.cs index 24ebd83..d335c71 100644 --- a/source/ChromeDevTools/Serialization/MessageContractResolver.cs +++ b/source/ChromeDevTools/Serialization/MessageContractResolver.cs @@ -1,9 +1,11 @@ -using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using System; +using System.Reflection; namespace MasterDevs.ChromeDevTools.Serialization { - internal class MessageContractResolver : DefaultContractResolver + public class MessageContractResolver : DefaultContractResolver { protected override string ResolvePropertyName(string propertyName) { @@ -17,5 +19,24 @@ namespace MasterDevs.ChromeDevTools.Serialization } return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1); } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var prop = base.CreateProperty(member, memberSerialization); + + // Add support for properties with private setters; for example, Response<T>.Result has a private setter and its + // value is not assigned by default. + if (!prop.Writable) + { + var property = member as PropertyInfo; + if (property != null) + { + var hasPrivateSetter = property.GetSetMethod(true) != null; + prop.Writable = hasPrivateSetter; + } + } + + return prop; + } } } \ No newline at end of file diff --git a/source/MasterDevs.ChromeDevTools.Tests/CommandResponseFactoryTests.cs b/source/MasterDevs.ChromeDevTools.Tests/CommandResponseFactoryTests.cs new file mode 100644 index 0000000..f4a5bb0 --- /dev/null +++ b/source/MasterDevs.ChromeDevTools.Tests/CommandResponseFactoryTests.cs @@ -0,0 +1,32 @@ +using System; +using MasterDevs.ChromeDevTools.Protocol.iOS.DOM; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.IO; + +namespace MasterDevs.ChromeDevTools.Tests +{ + [TestClass] + public class CommandResponseFactoryTests + { + [TestMethod] + [DeploymentItem("response-1.json")] + public void CreateTest() + { + string json = File.ReadAllText("response-1.json"); + + MethodTypeMap map = new MethodTypeMap("iOS"); + CommandFactory commandFactory = new CommandFactory(); + CommandResponseFactory responseFactory = new CommandResponseFactory(map, commandFactory); + + var command = commandFactory.Create<GetDocumentCommand>(); + var response = responseFactory.Create(json); + + Assert.IsInstanceOfType(response, typeof(CommandResponse<GetDocumentCommandResponse>)); + + var responseTyped = (CommandResponse<GetDocumentCommandResponse>)response; + + Assert.AreEqual(1, responseTyped.Id); + Assert.IsNotNull(responseTyped.Result); + } + } +} diff --git a/source/MasterDevs.ChromeDevTools.Tests/MasterDevs.ChromeDevTools.Tests.csproj b/source/MasterDevs.ChromeDevTools.Tests/MasterDevs.ChromeDevTools.Tests.csproj new file mode 100644 index 0000000..87d4cc0 --- /dev/null +++ b/source/MasterDevs.ChromeDevTools.Tests/MasterDevs.ChromeDevTools.Tests.csproj @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>MasterDevs.ChromeDevTools.Tests</RootNamespace> + <AssemblyName>MasterDevs.ChromeDevTools.Tests</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> + <IsCodedUITest>False</IsCodedUITest> + <TestProjectType>UnitTest</TestProjectType> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <Choose> + <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> + </ItemGroup> + </When> + <Otherwise> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> + </ItemGroup> + </Otherwise> + </Choose> + <ItemGroup> + <Compile Include="CommandResponseFactoryTests.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\ChromeDevTools\MasterDevs.ChromeDevTools.csproj"> + <Project>{c2508ee5-aab2-4275-88c9-7da9d1961336}</Project> + <Name>MasterDevs.ChromeDevTools</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="response-1.json"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + </ItemGroup> + <Choose> + <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + <Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + </ItemGroup> + </When> + </Choose> + <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file diff --git a/source/MasterDevs.ChromeDevTools.Tests/Properties/AssemblyInfo.cs b/source/MasterDevs.ChromeDevTools.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b4d51a0 --- /dev/null +++ b/source/MasterDevs.ChromeDevTools.Tests/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("MasterDevs.ChromeDevTools.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MasterDevs.ChromeDevTools.Tests")] +[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("4cfeba12-2ecb-4c17-9ea1-ad79c39eea72")] + +// 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/source/MasterDevs.ChromeDevTools.Tests/response-1.json b/source/MasterDevs.ChromeDevTools.Tests/response-1.json new file mode 100644 index 0000000..3305d41 --- /dev/null +++ b/source/MasterDevs.ChromeDevTools.Tests/response-1.json @@ -0,0 +1,61 @@ +{ + "result": { + "root": { + "nodeId": 16, + "nodeType": 9, + "nodeName": "#document", + "localName": "", + "nodeValue": "", + "childNodeCount": 2, + "children": [ + { + "nodeId": 17, + "nodeType": 10, + "nodeName": "html", + "localName": "", + "nodeValue": "", + "publicId": "", + "systemId": "", + "internalSubset": "" + }, + { + "nodeId": 18, + "nodeType": 1, + "nodeName": "HTML", + "localName": "html", + "nodeValue": "", + "childNodeCount": 2, + "children": [ + { + "nodeId": 19, + "nodeType": 1, + "nodeName": "HEAD", + "localName": "head", + "nodeValue": "", + "childNodeCount": 4, + "attributes": [] + }, + { + "nodeId": 20, + "nodeType": 1, + "nodeName": "BODY", + "localName": "body", + "nodeValue": "", + "childNodeCount": 2, + "attributes": [ "class", "transitions-on ios" ], + "role": "group" + } + ], + "attributes": [ "manifest", "manifest.appcache" ], + "role": "" + } + ], + "frameId": "0.1", + "documentURL": "https://mycompany/25/ios/v3.2.14/", + "baseURL": "https://mycompany/25/ios/v3.2.14/", + "xmlVersion": "", + "role": "" + } + }, + "id": 1 +} \ No newline at end of file diff --git a/source/MasterDevs.ChromeDevTools.sln b/source/MasterDevs.ChromeDevTools.sln index a20ee80..c56b4d4 100644 --- a/source/MasterDevs.ChromeDevTools.sln +++ b/source/MasterDevs.ChromeDevTools.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{0D43D20B-6D51-4CBC-BD30-F17B8CA65678}" ProjectSection(SolutionItems) = preProject @@ -24,6 +24,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterDevs.ChromeDevTools.S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterDevs.ChromeDevTools.ProtocolGenerator.Tests", "ProtocolGenerator.Tests\MasterDevs.ChromeDevTools.ProtocolGenerator.Tests.csproj", "{4C3A1910-79C5-43C0-8599-89921482B38B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterDevs.ChromeDevTools.Tests", "MasterDevs.ChromeDevTools.Tests\MasterDevs.ChromeDevTools.Tests.csproj", "{4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +48,10 @@ Global {4C3A1910-79C5-43C0-8599-89921482B38B}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C3A1910-79C5-43C0-8599-89921482B38B}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C3A1910-79C5-43C0-8599-89921482B38B}.Release|Any CPU.Build.0 = Release|Any CPU + {4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4CFEBA12-2ECB-4C17-9EA1-AD79C39EEA72}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE -- GitLab