diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index bd7cfbf..e371fc4 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -19,6 +19,8 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore + - name: Test + run: dotnet test --no-build - name: Pack run: dotnet pack SharpIpp/SharpIpp.csproj --configuration Release --no-build - name: Publish diff --git a/.github/workflows/test-commit.yml b/.github/workflows/test-commit.yml new file mode 100644 index 0000000..358f474 --- /dev/null +++ b/.github/workflows/test-commit.yml @@ -0,0 +1,24 @@ +name: Test commit + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: Test + run: dotnet test --no-build \ No newline at end of file diff --git a/SharpIpp.sln b/SharpIpp.sln index fd4e637..6363506 100644 --- a/SharpIpp.sln +++ b/SharpIpp.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.6.33723.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpIpp", "SharpIpp\SharpIpp.csproj", "{4718D86A-C465-43C7-8A36-959F53C8E6DB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpIpp.Tests", "SharpIpp.Tests\SharpIpp.Tests.csproj", "{04CB48CC-C2D2-4861-888A-0F5A65EA5851}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpIppServerExample", "SharpIppServerExample\SharpIppServerExample.csproj", "{DF4FFED2-D990-4D16-BE15-5DF5B244F9FF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpIppClientExample", "SharpIppClientExample\SharpIppClientExample.csproj", "{7ED644D7-06E3-42DE-9673-6A7DE3E6B7BA}" @@ -25,10 +23,6 @@ Global {4718D86A-C465-43C7-8A36-959F53C8E6DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {4718D86A-C465-43C7-8A36-959F53C8E6DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {4718D86A-C465-43C7-8A36-959F53C8E6DB}.Release|Any CPU.Build.0 = Release|Any CPU - {04CB48CC-C2D2-4861-888A-0F5A65EA5851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04CB48CC-C2D2-4861-888A-0F5A65EA5851}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04CB48CC-C2D2-4861-888A-0F5A65EA5851}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04CB48CC-C2D2-4861-888A-0F5A65EA5851}.Release|Any CPU.Build.0 = Release|Any CPU {DF4FFED2-D990-4D16-BE15-5DF5B244F9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF4FFED2-D990-4D16-BE15-5DF5B244F9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF4FFED2-D990-4D16-BE15-5DF5B244F9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/SharpIpp/Mapping/Profiles/TypesProfile.cs b/SharpIpp/Mapping/Profiles/TypesProfile.cs index e870dcf..01a7620 100644 --- a/SharpIpp/Mapping/Profiles/TypesProfile.cs +++ b/SharpIpp/Mapping/Profiles/TypesProfile.cs @@ -30,6 +30,8 @@ public void CreateMaps(IMapperConstructor mapper) mapper.CreateIppMap((src, map) => (ResolutionUnit)src); mapper.CreateIppMap((src, map) => (PrinterType)src); mapper.CreateIppMap( ( src, map ) => new IppVersion( src ) ); + mapper.CreateIppMap( ( src, map ) => 0 ); + mapper.CreateIppMap( ( src, jobs) => 0); //All name parameters can come as StringWithLanguage or string //mappers for string\language mapping diff --git a/SharpIpp/Mapping/SimpleMapper.cs b/SharpIpp/Mapping/SimpleMapper.cs index 604e019..f10ce25 100644 --- a/SharpIpp/Mapping/SimpleMapper.cs +++ b/SharpIpp/Mapping/SimpleMapper.cs @@ -62,7 +62,7 @@ private TDest Map(object source, Type sourceType, TDest dest) { return (TDest)source; } - + foreach (var (map, type) in PossiblePairs(sourceType, destType)) { switch (type) diff --git a/SharpIpp/Protocol/IppProtocol.cs b/SharpIpp/Protocol/IppProtocol.cs index c5be714..514a3a0 100644 --- a/SharpIpp/Protocol/IppProtocol.cs +++ b/SharpIpp/Protocol/IppProtocol.cs @@ -11,8 +11,6 @@ using SharpIpp.Protocol.Extensions; using SharpIpp.Protocol.Models; -[assembly: InternalsVisibleTo("SharpIpp.Tests")] - namespace SharpIpp.Protocol { /// diff --git a/SharpIpp/SharpIpp.csproj b/SharpIpp/SharpIpp.csproj index d36e8ca..4f37bf5 100644 --- a/SharpIpp/SharpIpp.csproj +++ b/SharpIpp/SharpIpp.csproj @@ -20,7 +20,7 @@ git SharpIppNext SharpIppNext - 1.0.3.0 + 1.0.4.0 true snupkg README.md diff --git a/SharpIpp/SharpIppClient.Cups.cs b/SharpIpp/SharpIppClient.Cups.cs index d3a3044..014459f 100644 --- a/SharpIpp/SharpIppClient.Cups.cs +++ b/SharpIpp/SharpIppClient.Cups.cs @@ -8,7 +8,7 @@ namespace SharpIpp public partial class SharpIppClient { /// - public Task GetCUPSPrintersAsync(CUPSGetPrintersRequest request, CancellationToken cancellationToken) => + public Task GetCUPSPrintersAsync(CUPSGetPrintersRequest request, CancellationToken cancellationToken = default) => SendAsync(request, ConstructIppRequest, Construct, cancellationToken); } } diff --git a/SharpIpp/SharpIppClient.cs b/SharpIpp/SharpIppClient.cs index 2d87ec0..6d6bd99 100644 --- a/SharpIpp/SharpIppClient.cs +++ b/SharpIpp/SharpIppClient.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; @@ -22,24 +23,29 @@ public partial class SharpIppClient : ISharpIppClient private readonly bool _disposeHttpClient; private readonly HttpClient _httpClient; - private readonly IIppProtocol _ippProtocol = new IppProtocol(); + private readonly IIppProtocol _ippProtocol; static SharpIppClient() { MapperSingleton = new Lazy(MapperFactory); } - public SharpIppClient() : this(new HttpClient(), true) + public SharpIppClient() : this(new HttpClient(), new IppProtocol(), true) { } - public SharpIppClient(HttpClient httpClient) : this(httpClient, false) + public SharpIppClient(HttpClient httpClient) : this(httpClient, new IppProtocol(), false ) { } - internal SharpIppClient(HttpClient httpClient, bool disposeHttpClient) + public SharpIppClient(HttpClient httpClient, IIppProtocol ippProtocol) : this( httpClient, ippProtocol, false ) + { + } + + internal SharpIppClient(HttpClient httpClient, IIppProtocol ippProtocol, bool disposeHttpClient) { _httpClient = httpClient; + _ippProtocol = ippProtocol; _disposeHttpClient = disposeHttpClient; } @@ -50,13 +56,12 @@ internal SharpIppClient(HttpClient httpClient, bool disposeHttpClient) /// but response still contains valid ipp-data in the body that can be parsed for better error description /// Seems like they are printer specific /// - public HttpStatusCode[] PlausibleHttpStatusCodes { get; set; } = - { + private static readonly HttpStatusCode[] _plausibleHttpStatusCodes = [ HttpStatusCode.Continue, HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden, HttpStatusCode.UpgradeRequired, - }; + ]; /// public async Task SendAsync( @@ -84,10 +89,7 @@ public async Task SendAsync( } catch (HttpRequestException ex) { - var plausibleHttpStatusCodes = PlausibleHttpStatusCodes; - var isPlausibleHttpStatusCode = Array.IndexOf(plausibleHttpStatusCodes, response.StatusCode) >= 0; - - if (!isPlausibleHttpStatusCode) + if (!_plausibleHttpStatusCodes.Contains(response.StatusCode)) { throw; } @@ -101,11 +103,10 @@ public async Task SendAsync( { using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); ippResponse = await _ippProtocol.ReadIppResponseAsync(responseStream, cancellationToken).ConfigureAwait(false); - + if (ippResponse == null) + throw new Exception( "Unable to parse response" ); if (!ippResponse.IsSuccessfulStatusCode()) - { - throw new IppResponseException($"Printer returned error code\n{ippResponse}", ippResponse); - } + throw new IppResponseException($"Printer returned error code", ippResponse); } catch { @@ -122,12 +123,7 @@ public async Task SendAsync( return ippResponse; } - if (ippResponse != null) - { - throw new IppResponseException(httpException.Message, httpException, ippResponse); - } - - throw httpException; + throw new IppResponseException(httpException.Message, httpException, ippResponse); } public void Dispose() diff --git a/SharpIppServerExample/Program.cs b/SharpIppServerExample/Program.cs index 0ce190e..f51904c 100644 --- a/SharpIppServerExample/Program.cs +++ b/SharpIppServerExample/Program.cs @@ -36,6 +36,9 @@ $"/{printerOptions.Name}", "/ipp/printer", $"/ipp/printer/{printerOptions.Name}" -}.ForEach( path => app.MapPost( path, async (HttpContext context, PrinterJobsService printerService) => - await printerService.ProcessRequestAsync(context.Request.Body, context.Response.Body))); +}.ForEach( path => app.MapPost( path, async ( HttpContext context, PrinterJobsService printerService ) => + { + context.Response.ContentType = "application/ipp"; + await printerService.ProcessRequestAsync( context.Request.Body, context.Response.Body ); + } ) ); app.Run(); diff --git a/SharpIppTests/Extensions/ObjectExtensions.cs b/SharpIppTests/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000..e063f26 --- /dev/null +++ b/SharpIppTests/Extensions/ObjectExtensions.cs @@ -0,0 +1,25 @@ +using FluentAssertions.Execution; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpIppTests.Extensions; + +[ExcludeFromCodeCoverage] +public static class ObjectExtensions +{ + public static bool VerifyAssertionScope( this T value, Action assertion ) + { + using var assertionScope = new AssertionScope(); + assertion.Invoke( value ); + var failures = assertionScope.Discard(); + if(!failures.Any()) + return true; + foreach(var failure in failures ) + System.Diagnostics.Trace.WriteLine( failure ); + return false; + } +} diff --git a/SharpIppTests/Protocol/IppProtocolTests.cs b/SharpIppTests/Protocol/IppProtocolTests.cs index 3d2ed9e..de5e9ee 100644 --- a/SharpIppTests/Protocol/IppProtocolTests.cs +++ b/SharpIppTests/Protocol/IppProtocolTests.cs @@ -14,773 +14,772 @@ using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; -namespace SharpIpp.Protocol.Tests +namespace SharpIpp.Protocol.Tests; + +[TestClass] +[ExcludeFromCodeCoverage] +public class IppProtocolTests { - [TestClass] - [ExcludeFromCodeCoverage] - public class IppProtocolTests + [TestMethod()] + public void WriteValue_NoValue_ShouldBeWritten() { - [TestMethod()] - public void WriteValue_NoValue_ShouldBeWritten() - { - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - protocol.WriteValue( NoValue.Instance, binaryWriter ); - memoryStream.ToArray().Should().Equal( 0x00, 0x00 ); - } - - [DataTestMethod] - [DataRow( true, new byte[] { 0x00, 0x01, 0x01 } )] - [DataRow( false, new byte[] { 0x00, 0x01, 0x00 } )] - public void WriteValue_Boolean_ShouldBeWritten( bool value, byte[] expected ) - { - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - protocol.WriteValue( value, binaryWriter ); - memoryStream.ToArray().Should().Equal( expected ); - } - - [DataTestMethod] - [DataRow( "12/31/1999 23:59:59 +02:30", new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2B, 0x02, 0x1E } )] - [DataRow( "12/31/1999 23:59:59 -02:30", new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2D, 0x02, 0x1E } )] - [DataRow( "01/01/0001 01:01:01 +00:00", new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 } )] - public void WriteValue_DateTimeOffset_ShouldBeWritten( string value, byte[] expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( DateTimeOffset.Parse( value, CultureInfo.InvariantCulture ), binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( expected ); - } - - [DataTestMethod] - [DataRow( int.MinValue, new byte[] { 0x00, 0x04, 0x80, 0x00, 0x00, 0x00 } )] - [DataRow( 0, new byte[] { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 } )] - [DataRow( int.MaxValue, new byte[] { 0x00, 0x04, 0x7F, 0xFF, 0xFF, 0xFF } )] - public void WriteValue_Int32_ShouldBeWritten( int value, byte[] expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( value, binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( expected ); - } - - [DataTestMethod] - [DataRow( int.MinValue, int.MinValue, new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 } )] - [DataRow( int.MinValue, int.MaxValue, new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF } )] - [DataRow( int.MaxValue, int.MaxValue, new byte[] { 0x00, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF } )] - public void WriteValue_Range_ShouldBeWritten( int lower, int upper, byte[] expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( new Models.Range( lower, upper ), binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( expected ); - } - - [DataTestMethod] - [DataRow( 0, int.MaxValue, ResolutionUnit.DotsPerCm, new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 } )] - [DataRow( 0, int.MaxValue, ResolutionUnit.DotsPerInch, new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 } )] - [DataRow( int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerCm, new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 } )] - [DataRow( int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerInch, new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 } )] - public void WriteValue_Resolution_ShouldBeWritten( int width, int height, ResolutionUnit unit, byte[] expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( new Resolution( width, height, unit ), binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( expected ); - } - - [DataTestMethod] - [DataRow( "en-us", "Lorem", new byte[] { 0x00, 0x0A, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } )] - public void Write_StringWithLanguage_ShouldBeWritten( string language, string text, byte[] expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( new StringWithLanguage( language, text ), binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( expected ); - } - - [TestMethod] - public void Write_String_ShouldBeWritten( ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteValue( "Lorem", binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D ); - } - - [TestMethod] - public void WriteValue_UnsupportedType_ThrowsArgumentException() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - Action act = () => protocol.WriteValue( 123L, binaryWriter ); - // Assert - act.Should().Throw(); - } - - [TestMethod] - public async Task WriteIppRequestAsync_NoAttributes_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - var message = new IppRequestMessage - { - IppOperation = IppOperation.PrintJob, - Version = IppVersion.V1_1, - RequestId = 123 - }; - // Act - await protocol.WriteIppRequestAsync( message, memoryStream ); - // Assert - memoryStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B ); - } - - [TestMethod] - public async Task WriteIppRequestAsync_TwoSections_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - var message = new IppRequestMessage - { - IppOperation = IppOperation.PrintJob, - Version = IppVersion.V1_1, - RequestId = 123 - }; - message.OperationAttributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.JobName, "Test Job" ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); - - - //message.OperationAttributes.Add( new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "ipp://127.0.0.1:631/" ) ); - /* - message.OperationAttributes.Add( new IppAttribute( Tag.BegCollection, JobAttribute.IppAttributeFidelity, false ) ); //todo: check beg - message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.DocumentName, "Document Name" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.MimeMediaType, JobAttribute.DocumentFormat, "application/octet-stream" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.DocumentNaturalLanguage, "en" ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.MultipleDocumentHandling, MultipleDocumentHandling.SeparateDocumentsCollatedCopies.ToString() ) ); - - message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.Finishings, (int)Finishings.None ) ); - message.JobAttributes.Add( new IppAttribute( Tag.RangeOfInteger, JobAttribute.PageRanges, new Models.Range( 1, 2 ) ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.Sides, Sides.OneSided.ToString() ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.NumberUp, 1 ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.OrientationRequested, (int)Orientation.Portrait ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Resolution, JobAttribute.PrinterResolution, new Resolution( 600, 600, ResolutionUnit.DotsPerInch ) ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.PrintQuality, (int)PrintQuality.Normal ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.PrintScaling, PrintScaling.Fit.ToString() ) ); - */ - // Act - await protocol.WriteIppRequestAsync( message, memoryStream ); - // Assert - memoryStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, - 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, - 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6E, 0x42, 0x00, 0x08, 0x6A, 0x6F, 0x62, 0x2D, 0x6E, - 0x61, 0x6D, 0x65, 0x00, 0x08, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4A, 0x6F, 0x62, 0x02, 0x21, 0x00, 0x06, - 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 ); - } - - [TestMethod] - public async Task WriteIppRequestAsync_Document_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new(); - using MemoryStream documentStream = new( Encoding.ASCII.GetBytes( "Lorem" ) ); - var message = new IppRequestMessage - { - IppOperation = IppOperation.PrintJob, - Version = IppVersion.V1_1, - RequestId = 123, - Document = documentStream - }; - // Act - await protocol.WriteIppRequestAsync( message, requestStream ); - // Assert - requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x4C, 0x6F, 0x72, 0x65, 0x6D ); - } - - [TestMethod] - public async Task ReadIppRequestAsync_TwoSections_ShouldMatch() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new( new byte[] { - 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, - 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, - 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6E, 0x42, 0x00, 0x08, 0x6A, 0x6F, 0x62, 0x2D, 0x6E, - 0x61, 0x6D, 0x65, 0x00, 0x08, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4A, 0x6F, 0x62, 0x02, 0x21, 0x00, 0x06, - 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 } ); - // Act - Func> act = async () => await protocol.ReadIppRequestAsync( requestStream ); - // Assert - using MemoryStream documentStream = new(); - var message = new IppRequestMessage - { - IppOperation = IppOperation.PrintJob, - Version = IppVersion.V1_1, - RequestId = 123, - Document = documentStream - }; - message.OperationAttributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); - message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.JobName, "Test Job" ) ); - message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); - (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message, x => x.Excluding( ( IMemberInfo x ) => x.Path == "Document.ReadTimeout" || x.Path == "Document.WriteTimeout" ) ); - } - - [TestMethod()] - public async Task ReadIppRequestAsync_Document_ShouldMatch() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); - // Act - Func> act = async () => await protocol.ReadIppRequestAsync( requestStream ); - // Assert - using var expectedStream = new MemoryStream( new byte[] { 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); - (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( new IppRequestMessage - { - IppOperation = IppOperation.PrintJob, - Version = IppVersion.V1_1, - RequestId = 123, - Document = expectedStream - }, x => x.Excluding( ( IMemberInfo x ) => x.Path == "Document.ReadTimeout" || x.Path == "Document.WriteTimeout" ) ); - } - - [TestMethod()] - public void WriteSection_NoAttributes_ShouldNotWriteAnything() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteSection( SectionTag.OperationAttributesTag, new List(), binaryWriter ); - // Assert - memoryStream.Length.Should().Be( 0 ); - } - - [TestMethod()] - public void WriteSection_OneAttribute_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteSection( SectionTag.OperationAttributesTag, new List - { - new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion(1,0).ToString() ) - }, binaryWriter ); - // Assert - memoryStream.ToArray().Should().Equal( 0x01, 0x44, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x30 ); - } - - [TestMethod()] - public void WriteAttribute_OneAttribute_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteAttribute( - binaryWriter, - new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ), - null ); - // Assert - memoryStream.ToArray().Should().Equal( 0x44, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x31 ); - } - - [TestMethod()] - public void WriteAttribute_SecondSimilarAttribute_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryWriter binaryWriter = new( memoryStream ); - // Act - protocol.WriteAttribute( - binaryWriter, - new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ), - new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 0 ).ToString() ) ); - // Assert - memoryStream.ToArray().Should().Equal( 0x44, 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 ); - } - - [TestMethod] - public async Task WriteIppResponseAsync_NoSections_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new(); - var message = new IppResponseMessage - { - Version = IppVersion.V1_1, - RequestId = 123 - }; - // Act - await protocol.WriteIppResponseAsync( message, requestStream ); - // Assert - requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x03 ); - } - - [TestMethod] - public async Task WriteIppResponseAsync_TwoSections_ShouldBeWritten() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new(); - var message = new IppResponseMessage - { - Version = IppVersion.V1_1, - RequestId = 123 - }; - var operationSection = new IppSection { Tag = SectionTag.OperationAttributesTag }; - operationSection.Attributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); - operationSection.Attributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); - message.Sections.Add( operationSection ); - var jobSection = new IppSection { Tag = SectionTag.JobAttributesTag }; - jobSection.Attributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); - message.Sections.Add( jobSection ); - // Act - await protocol.WriteIppResponseAsync( message, requestStream ); - // Assert - requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, 0x12, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, - 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, - 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, - 0x6E, 0x02, 0x21, 0x00, 0x06, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 ); - } - - [TestMethod] - public async Task ReadIppResponseAsync_NoSection_ShouldMatch() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x03 } ); - // Act - Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); - // Assert - var message = new IppResponseMessage - { - Version = IppVersion.V1_1, - RequestId = 123 - }; - (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message ); - } - - [TestMethod] - public async Task ReadIppResponseAsync_TwoSection_ShouldMatch() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new( new byte[] { - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, 0x12, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, - 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, - 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, - 0x6E, 0x02, 0x21, 0x00, 0x06, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 } ); - // Act - Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); - // Assert - var message = new IppResponseMessage - { - Version = IppVersion.V1_1, - RequestId = 123 - }; - var operationSection = new IppSection { Tag = SectionTag.OperationAttributesTag }; - operationSection.Attributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); - operationSection.Attributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); - message.Sections.Add( operationSection ); - var jobSection = new IppSection { Tag = SectionTag.JobAttributesTag }; - jobSection.Attributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); - message.Sections.Add( jobSection ); - (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message ); - } - - [TestMethod] - public async Task ReadIppResponseAsync_MissingSectionTag_ShouldThrowException() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x47 } ); - // Act - Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); - // Assert - await act.Should().ThrowAsync(); - } - - [TestMethod] - public async Task ReadIppResponseAsync_EmptyStream_ShouldThrowException() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream requestStream = new(); - // Act - Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); - // Assert - await act.Should().ThrowAsync(); - } + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + protocol.WriteValue( NoValue.Instance, binaryWriter ); + memoryStream.ToArray().Should().Equal( 0x00, 0x00 ); + } + + [DataTestMethod] + [DataRow( true, new byte[] { 0x00, 0x01, 0x01 } )] + [DataRow( false, new byte[] { 0x00, 0x01, 0x00 } )] + public void WriteValue_Boolean_ShouldBeWritten( bool value, byte[] expected ) + { + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + protocol.WriteValue( value, binaryWriter ); + memoryStream.ToArray().Should().Equal( expected ); + } + + [DataTestMethod] + [DataRow( "12/31/1999 23:59:59 +02:30", new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2B, 0x02, 0x1E } )] + [DataRow( "12/31/1999 23:59:59 -02:30", new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2D, 0x02, 0x1E } )] + [DataRow( "01/01/0001 01:01:01 +00:00", new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 } )] + public void WriteValue_DateTimeOffset_ShouldBeWritten( string value, byte[] expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( DateTimeOffset.Parse( value, CultureInfo.InvariantCulture ), binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( expected ); + } + + [DataTestMethod] + [DataRow( int.MinValue, new byte[] { 0x00, 0x04, 0x80, 0x00, 0x00, 0x00 } )] + [DataRow( 0, new byte[] { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 } )] + [DataRow( int.MaxValue, new byte[] { 0x00, 0x04, 0x7F, 0xFF, 0xFF, 0xFF } )] + public void WriteValue_Int32_ShouldBeWritten( int value, byte[] expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( value, binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( expected ); + } + + [DataTestMethod] + [DataRow( int.MinValue, int.MinValue, new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 } )] + [DataRow( int.MinValue, int.MaxValue, new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF } )] + [DataRow( int.MaxValue, int.MaxValue, new byte[] { 0x00, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF } )] + public void WriteValue_Range_ShouldBeWritten( int lower, int upper, byte[] expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( new Models.Range( lower, upper ), binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( expected ); + } + + [DataTestMethod] + [DataRow( 0, int.MaxValue, ResolutionUnit.DotsPerCm, new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 } )] + [DataRow( 0, int.MaxValue, ResolutionUnit.DotsPerInch, new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 } )] + [DataRow( int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerCm, new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 } )] + [DataRow( int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerInch, new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 } )] + public void WriteValue_Resolution_ShouldBeWritten( int width, int height, ResolutionUnit unit, byte[] expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( new Resolution( width, height, unit ), binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( expected ); + } + + [DataTestMethod] + [DataRow( "en-us", "Lorem", new byte[] { 0x00, 0x0A, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } )] + public void Write_StringWithLanguage_ShouldBeWritten( string language, string text, byte[] expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( new StringWithLanguage( language, text ), binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( expected ); + } + + [TestMethod] + public void Write_String_ShouldBeWritten( ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteValue( "Lorem", binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D ); + } + + [TestMethod] + public void WriteValue_UnsupportedType_ThrowsArgumentException() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + Action act = () => protocol.WriteValue( 123L, binaryWriter ); + // Assert + act.Should().Throw(); + } + + [TestMethod] + public async Task WriteIppRequestAsync_NoAttributes_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + var message = new IppRequestMessage + { + IppOperation = IppOperation.PrintJob, + Version = IppVersion.V1_1, + RequestId = 123 + }; + // Act + await protocol.WriteIppRequestAsync( message, memoryStream ); + // Assert + memoryStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B ); + } + + [TestMethod] + public async Task WriteIppRequestAsync_TwoSections_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + var message = new IppRequestMessage + { + IppOperation = IppOperation.PrintJob, + Version = IppVersion.V1_1, + RequestId = 123 + }; + message.OperationAttributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.JobName, "Test Job" ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); + + + //message.OperationAttributes.Add( new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "ipp://127.0.0.1:631/" ) ); + /* + message.OperationAttributes.Add( new IppAttribute( Tag.BegCollection, JobAttribute.IppAttributeFidelity, false ) ); //todo: check beg + message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.DocumentName, "Document Name" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.MimeMediaType, JobAttribute.DocumentFormat, "application/octet-stream" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.DocumentNaturalLanguage, "en" ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.MultipleDocumentHandling, MultipleDocumentHandling.SeparateDocumentsCollatedCopies.ToString() ) ); - [DataTestMethod] - [DataRow( Tag.TextWithLanguage )] - [DataRow( Tag.NameWithLanguage )] - public void ReadValue_StringWithLanguage_ReturnsCorrectResult( Tag tag ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x0A, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, tag ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( new StringWithLanguage( "en-us", "Lorem" ) ); - } - - [DataTestMethod] - [DataRow( Tag.OctetStringWithAnUnspecifiedFormat )] - [DataRow( Tag.BegCollection )] - [DataRow( Tag.TextWithoutLanguage )] - [DataRow( Tag.NameWithoutLanguage )] - [DataRow( Tag.Keyword )] - [DataRow( Tag.Uri )] - [DataRow( Tag.UriScheme )] - [DataRow( Tag.Charset )] - [DataRow( Tag.NaturalLanguage )] - [DataRow( Tag.MimeMediaType )] - [DataRow( Tag.MemberAttrName )] - [DataRow( Tag.OctetStringUnassigned38 )] - [DataRow( Tag.OctetStringUnassigned39 )] - [DataRow( Tag.OctetStringUnassigned3A )] - [DataRow( Tag.OctetStringUnassigned3B )] - [DataRow( Tag.OctetStringUnassigned3C )] - [DataRow( Tag.OctetStringUnassigned3D )] - [DataRow( Tag.OctetStringUnassigned3E )] - [DataRow( Tag.OctetStringUnassigned3F )] - [DataRow( Tag.StringUnassigned40 )] - [DataRow( Tag.StringUnassigned43 )] - [DataRow( Tag.StringUnassigned4B )] - [DataRow( Tag.StringUnassigned4C )] - [DataRow( Tag.StringUnassigned4D )] - [DataRow( Tag.StringUnassigned4E )] - [DataRow( Tag.StringUnassigned4F )] - [DataRow( Tag.StringUnassigned50 )] - [DataRow( Tag.StringUnassigned51 )] - [DataRow( Tag.StringUnassigned52 )] - [DataRow( Tag.StringUnassigned53 )] - [DataRow( Tag.StringUnassigned54 )] - [DataRow( Tag.StringUnassigned55 )] - [DataRow( Tag.StringUnassigned56 )] - [DataRow( Tag.StringUnassigned57 )] - [DataRow( Tag.StringUnassigned58 )] - [DataRow( Tag.StringUnassigned59 )] - [DataRow( Tag.StringUnassigned5A )] - [DataRow( Tag.StringUnassigned5B )] - [DataRow( Tag.StringUnassigned5C )] - [DataRow( Tag.StringUnassigned5D )] - [DataRow( Tag.StringUnassigned5E )] - [DataRow( Tag.StringUnassigned5F )] - public void ReadValue_String_ReturnsCorrectResult( Tag tag ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, tag ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( "Lorem" ); - } - - [DataTestMethod] - [DataRow( Tag.Unsupported )] - [DataRow( Tag.Unknown )] - [DataRow( Tag.NoValue )] - [DataRow( Tag.EndCollection )] - public void ReadValue_NoValue_ReturnsCorrectResult( Tag tag ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00 } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, tag ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( NoValue.Instance ); - } - - [TestMethod] - public void ReadValue_BrokenNoValue_ThrowsArgumentException() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x01, 0x00 } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.NoValue ); - // Assert - act.Should().Throw(); - } - - - [DataTestMethod] - [DataRow( Tag.Integer )] - [DataRow( Tag.Enum )] - [DataRow( Tag.IntegerUnassigned20 )] - [DataRow( Tag.IntegerUnassigned24 )] - [DataRow( Tag.IntegerUnassigned25 )] - [DataRow( Tag.IntegerUnassigned26 )] - [DataRow( Tag.IntegerUnassigned27 )] - [DataRow( Tag.IntegerUnassigned28 )] - [DataRow( Tag.IntegerUnassigned29 )] - [DataRow( Tag.IntegerUnassigned2A )] - [DataRow( Tag.IntegerUnassigned2B )] - [DataRow( Tag.IntegerUnassigned2C )] - [DataRow( Tag.IntegerUnassigned2D )] - [DataRow( Tag.IntegerUnassigned2E )] - [DataRow( Tag.IntegerUnassigned2F )] - public void ReadValue_Int_ReturnsCorrectResult( Tag tag ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x04, 0x00, 0x00, 0x00, 0x10 } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, tag ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( 16 ); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid second byte" )] - public void ReadValue_Int_ThrowsArgumentException( byte[] value ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value, 0, value.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.Integer ); - // Assert - act.Should().Throw(); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x01, 0x00 }, false )] - [DataRow( new byte[] { 0x00, 0x01, 0x01 }, true )] - public void ReadValue_Bool_ReturnsCorrectResult( byte[] value, bool expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value, 0, value.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.Boolean ); - // Assert - act.Should().NotThrow().Which.Should().Be( expected ); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x01, 0x02 } )] - [DataRow( new byte[] { 0x00, 0x00, 0x00 } )] - public void ReadValue_InvalidBool_ThrowsArgumentException( byte[] value ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value, 0, value.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.Boolean ); - // Assert - act.Should().Throw(); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2B, 0x02, 0x1E }, "12/31/1999 23:59:59 +02:30", DisplayName = "Time with negative offset" )] - [DataRow( new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2D, 0x02, 0x1E }, "12/31/1999 23:59:59 -02:30", DisplayName = "Time with positive offset" )] - [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, "01/01/0001 01:01:01 +00:00", DisplayName = "Minimal DateTime" )] - public void ReadValue_DateTimeOffset_ReturnsCorrectResult( byte[] value, string expected ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value, 0, value.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.DateTime ); - // Assert - act.Should().NotThrow().Which.Should().Be( DateTimeOffset.Parse( expected, CultureInfo.InvariantCulture ) ); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, DisplayName = "Invalid second byte" )] - [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, DisplayName = "Invalid offset sign" )] - [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, DisplayName = "Invalid month" )] - public void ReadValue_InvalidDateTimeOffset_ThrowsArgumentException( byte[] value ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.DateTime ); - // Assert - act.Should().Throw(); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }, int.MinValue, int.MinValue )] - [DataRow( new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF }, int.MinValue, int.MaxValue )] - [DataRow( new byte[] { 0x00, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, int.MaxValue, int.MaxValue )] - public void ReadValue_Range_ReturnsCorrectResult( byte[] value, int lower, int upper ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value, 0, value.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.RangeOfInteger ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( new Models.Range( lower, upper ) ); - } - - - [TestMethod()] - [DataRow( new byte[] { 0x01, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid first byte" )] - [DataRow( new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid second byte" )] - public void ReadValue_InvalidRange_ShouldThrowArgumentException( byte[] value ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( value ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.RangeOfInteger ); - // Assert - act.Should().Throw(); - } - - [TestMethod()] - [DataRow( new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, 0, int.MaxValue, ResolutionUnit.DotsPerCm )] - [DataRow( new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 }, 0, int.MaxValue, ResolutionUnit.DotsPerInch )] - [DataRow( new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerCm )] - [DataRow( new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 }, int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerInch )] - public void ReadValue_Resolution_ReturnsCorrectResult( byte[] bytes, int width, int height, ResolutionUnit unit ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( bytes, 0, bytes.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.Resolution ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( new Resolution( width, height, unit ) ); - } - - [TestMethod()] - [DataRow( new byte[] { 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, DisplayName = "Invalid first byte" )] - [DataRow( new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, DisplayName = "Invalid second byte" )] - public void ReadValue_InvalidResolution_ThrowsArgumentException( byte[] bytes ) - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( bytes, 0, bytes.Length ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, Tag.Resolution ); - // Assert - act.Should().Throw(); - } - - [TestMethod] - public void ReadValue_InvalidTag_ThrowsArgumentException() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new(); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadValue( binaryReader, (Tag)0x01 ); - // Assert - act.Should().Throw(); - } - - [TestMethod] - public void ReadAttribute_OneAttribute_ReturnsCorrectResult() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, null ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ) ); - } - - [TestMethod] - public void ReadAttribute_SecondSimilarAttribute_ReturnsCorrectResult() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); - using BinaryReader binaryReader = new( memoryStream ); - var previousAttribute = new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 0 ).ToString() ); - // Act - Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, previousAttribute ); - // Assert - act.Should().NotThrow().Which.Should().BeEquivalentTo( new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ) ); - } - - [TestMethod] - public void ReadAttribute_AttributeWithoutName_ReturnsCorrectResult() - { - // Arrange - var protocol = new IppProtocol(); - using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); - using BinaryReader binaryReader = new( memoryStream ); - // Act - Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, null ); - // Assert - act.Should().Throw(); - } + message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.Finishings, (int)Finishings.None ) ); + message.JobAttributes.Add( new IppAttribute( Tag.RangeOfInteger, JobAttribute.PageRanges, new Models.Range( 1, 2 ) ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.Sides, Sides.OneSided.ToString() ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.NumberUp, 1 ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.OrientationRequested, (int)Orientation.Portrait ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Resolution, JobAttribute.PrinterResolution, new Resolution( 600, 600, ResolutionUnit.DotsPerInch ) ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Enum, JobAttribute.PrintQuality, (int)PrintQuality.Normal ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Keyword, JobAttribute.PrintScaling, PrintScaling.Fit.ToString() ) ); + */ + // Act + await protocol.WriteIppRequestAsync( message, memoryStream ); + // Assert + memoryStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, + 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6E, 0x42, 0x00, 0x08, 0x6A, 0x6F, 0x62, 0x2D, 0x6E, + 0x61, 0x6D, 0x65, 0x00, 0x08, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4A, 0x6F, 0x62, 0x02, 0x21, 0x00, 0x06, + 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 ); + } + + [TestMethod] + public async Task WriteIppRequestAsync_Document_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new(); + using MemoryStream documentStream = new( Encoding.ASCII.GetBytes( "Lorem" ) ); + var message = new IppRequestMessage + { + IppOperation = IppOperation.PrintJob, + Version = IppVersion.V1_1, + RequestId = 123, + Document = documentStream + }; + // Act + await protocol.WriteIppRequestAsync( message, requestStream ); + // Assert + requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x4C, 0x6F, 0x72, 0x65, 0x6D ); + } + + [TestMethod] + public async Task ReadIppRequestAsync_TwoSections_ShouldMatch() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new( new byte[] { + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, + 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6E, 0x42, 0x00, 0x08, 0x6A, 0x6F, 0x62, 0x2D, 0x6E, + 0x61, 0x6D, 0x65, 0x00, 0x08, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4A, 0x6F, 0x62, 0x02, 0x21, 0x00, 0x06, + 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 } ); + // Act + Func> act = async () => await protocol.ReadIppRequestAsync( requestStream ); + // Assert + using MemoryStream documentStream = new(); + var message = new IppRequestMessage + { + IppOperation = IppOperation.PrintJob, + Version = IppVersion.V1_1, + RequestId = 123, + Document = documentStream + }; + message.OperationAttributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); + message.OperationAttributes.Add( new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.JobName, "Test Job" ) ); + message.JobAttributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message, x => x.Excluding( ( IMemberInfo x ) => x.Path == "Document.ReadTimeout" || x.Path == "Document.WriteTimeout" ) ); + } + + [TestMethod()] + public async Task ReadIppRequestAsync_Document_ShouldMatch() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7B, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); + // Act + Func> act = async () => await protocol.ReadIppRequestAsync( requestStream ); + // Assert + using var expectedStream = new MemoryStream( new byte[] { 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( new IppRequestMessage + { + IppOperation = IppOperation.PrintJob, + Version = IppVersion.V1_1, + RequestId = 123, + Document = expectedStream + }, x => x.Excluding( ( IMemberInfo x ) => x.Path == "Document.ReadTimeout" || x.Path == "Document.WriteTimeout" ) ); + } + + [TestMethod()] + public void WriteSection_NoAttributes_ShouldNotWriteAnything() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteSection( SectionTag.OperationAttributesTag, new List(), binaryWriter ); + // Assert + memoryStream.Length.Should().Be( 0 ); + } + + [TestMethod()] + public void WriteSection_OneAttribute_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteSection( SectionTag.OperationAttributesTag, new List + { + new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion(1,0).ToString() ) + }, binaryWriter ); + // Assert + memoryStream.ToArray().Should().Equal( 0x01, 0x44, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x30 ); + } + + [TestMethod()] + public void WriteAttribute_OneAttribute_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteAttribute( + binaryWriter, + new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ), + null ); + // Assert + memoryStream.ToArray().Should().Equal( 0x44, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x31 ); + } + + [TestMethod()] + public void WriteAttribute_SecondSimilarAttribute_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryWriter binaryWriter = new( memoryStream ); + // Act + protocol.WriteAttribute( + binaryWriter, + new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ), + new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 0 ).ToString() ) ); + // Assert + memoryStream.ToArray().Should().Equal( 0x44, 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 ); + } + + [TestMethod] + public async Task WriteIppResponseAsync_NoSections_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new(); + var message = new IppResponseMessage + { + Version = IppVersion.V1_1, + RequestId = 123 + }; + // Act + await protocol.WriteIppResponseAsync( message, requestStream ); + // Assert + requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x03 ); + } + + [TestMethod] + public async Task WriteIppResponseAsync_TwoSections_ShouldBeWritten() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new(); + var message = new IppResponseMessage + { + Version = IppVersion.V1_1, + RequestId = 123 + }; + var operationSection = new IppSection { Tag = SectionTag.OperationAttributesTag }; + operationSection.Attributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); + operationSection.Attributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); + message.Sections.Add( operationSection ); + var jobSection = new IppSection { Tag = SectionTag.JobAttributesTag }; + jobSection.Attributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); + message.Sections.Add( jobSection ); + // Act + await protocol.WriteIppResponseAsync( message, requestStream ); + // Assert + requestStream.ToArray().Should().Equal( 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, 0x12, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, + 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, + 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, + 0x6E, 0x02, 0x21, 0x00, 0x06, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 ); + } + + [TestMethod] + public async Task ReadIppResponseAsync_NoSection_ShouldMatch() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x03 } ); + // Act + Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); + // Assert + var message = new IppResponseMessage + { + Version = IppVersion.V1_1, + RequestId = 123 + }; + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message ); + } + + [TestMethod] + public async Task ReadIppResponseAsync_TwoSection_ShouldMatch() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new( new byte[] { + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x47, 0x00, 0x12, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, + 0x75, 0x74, 0x66, 0x2D, 0x38, 0x48, 0x00, 0x1B, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2D, + 0x6E, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6C, 0x2D, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x02, 0x65, + 0x6E, 0x02, 0x21, 0x00, 0x06, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x03 } ); + // Act + Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); + // Assert + var message = new IppResponseMessage + { + Version = IppVersion.V1_1, + RequestId = 123 + }; + var operationSection = new IppSection { Tag = SectionTag.OperationAttributesTag }; + operationSection.Attributes.Add( new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ) ); + operationSection.Attributes.Add( new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ) ); + message.Sections.Add( operationSection ); + var jobSection = new IppSection { Tag = SectionTag.JobAttributesTag }; + jobSection.Attributes.Add( new IppAttribute( Tag.Integer, JobAttribute.Copies, 1 ) ); + message.Sections.Add( jobSection ); + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( message ); + } + + [TestMethod] + public async Task ReadIppResponseAsync_MissingSectionTag_ShouldThrowException() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new( new byte[] { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x47 } ); + // Act + Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod] + public async Task ReadIppResponseAsync_EmptyStream_ShouldThrowException() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream requestStream = new(); + // Act + Func> act = async () => await protocol.ReadIppResponseAsync( requestStream ); + // Assert + await act.Should().ThrowAsync(); + } + + [DataTestMethod] + [DataRow( Tag.TextWithLanguage )] + [DataRow( Tag.NameWithLanguage )] + public void ReadValue_StringWithLanguage_ReturnsCorrectResult( Tag tag ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x0A, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, tag ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( new StringWithLanguage( "en-us", "Lorem" ) ); + } + + [DataTestMethod] + [DataRow( Tag.OctetStringWithAnUnspecifiedFormat )] + [DataRow( Tag.BegCollection )] + [DataRow( Tag.TextWithoutLanguage )] + [DataRow( Tag.NameWithoutLanguage )] + [DataRow( Tag.Keyword )] + [DataRow( Tag.Uri )] + [DataRow( Tag.UriScheme )] + [DataRow( Tag.Charset )] + [DataRow( Tag.NaturalLanguage )] + [DataRow( Tag.MimeMediaType )] + [DataRow( Tag.MemberAttrName )] + [DataRow( Tag.OctetStringUnassigned38 )] + [DataRow( Tag.OctetStringUnassigned39 )] + [DataRow( Tag.OctetStringUnassigned3A )] + [DataRow( Tag.OctetStringUnassigned3B )] + [DataRow( Tag.OctetStringUnassigned3C )] + [DataRow( Tag.OctetStringUnassigned3D )] + [DataRow( Tag.OctetStringUnassigned3E )] + [DataRow( Tag.OctetStringUnassigned3F )] + [DataRow( Tag.StringUnassigned40 )] + [DataRow( Tag.StringUnassigned43 )] + [DataRow( Tag.StringUnassigned4B )] + [DataRow( Tag.StringUnassigned4C )] + [DataRow( Tag.StringUnassigned4D )] + [DataRow( Tag.StringUnassigned4E )] + [DataRow( Tag.StringUnassigned4F )] + [DataRow( Tag.StringUnassigned50 )] + [DataRow( Tag.StringUnassigned51 )] + [DataRow( Tag.StringUnassigned52 )] + [DataRow( Tag.StringUnassigned53 )] + [DataRow( Tag.StringUnassigned54 )] + [DataRow( Tag.StringUnassigned55 )] + [DataRow( Tag.StringUnassigned56 )] + [DataRow( Tag.StringUnassigned57 )] + [DataRow( Tag.StringUnassigned58 )] + [DataRow( Tag.StringUnassigned59 )] + [DataRow( Tag.StringUnassigned5A )] + [DataRow( Tag.StringUnassigned5B )] + [DataRow( Tag.StringUnassigned5C )] + [DataRow( Tag.StringUnassigned5D )] + [DataRow( Tag.StringUnassigned5E )] + [DataRow( Tag.StringUnassigned5F )] + public void ReadValue_String_ReturnsCorrectResult( Tag tag ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, tag ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( "Lorem" ); + } + + [DataTestMethod] + [DataRow( Tag.Unsupported )] + [DataRow( Tag.Unknown )] + [DataRow( Tag.NoValue )] + [DataRow( Tag.EndCollection )] + public void ReadValue_NoValue_ReturnsCorrectResult( Tag tag ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00 } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, tag ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( NoValue.Instance ); + } + + [TestMethod] + public void ReadValue_BrokenNoValue_ThrowsArgumentException() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x01, 0x00 } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.NoValue ); + // Assert + act.Should().Throw(); + } + + + [DataTestMethod] + [DataRow( Tag.Integer )] + [DataRow( Tag.Enum )] + [DataRow( Tag.IntegerUnassigned20 )] + [DataRow( Tag.IntegerUnassigned24 )] + [DataRow( Tag.IntegerUnassigned25 )] + [DataRow( Tag.IntegerUnassigned26 )] + [DataRow( Tag.IntegerUnassigned27 )] + [DataRow( Tag.IntegerUnassigned28 )] + [DataRow( Tag.IntegerUnassigned29 )] + [DataRow( Tag.IntegerUnassigned2A )] + [DataRow( Tag.IntegerUnassigned2B )] + [DataRow( Tag.IntegerUnassigned2C )] + [DataRow( Tag.IntegerUnassigned2D )] + [DataRow( Tag.IntegerUnassigned2E )] + [DataRow( Tag.IntegerUnassigned2F )] + public void ReadValue_Int_ReturnsCorrectResult( Tag tag ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x04, 0x00, 0x00, 0x00, 0x10 } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, tag ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( 16 ); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid second byte" )] + public void ReadValue_Int_ThrowsArgumentException( byte[] value ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value, 0, value.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.Integer ); + // Assert + act.Should().Throw(); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x01, 0x00 }, false )] + [DataRow( new byte[] { 0x00, 0x01, 0x01 }, true )] + public void ReadValue_Bool_ReturnsCorrectResult( byte[] value, bool expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value, 0, value.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.Boolean ); + // Assert + act.Should().NotThrow().Which.Should().Be( expected ); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x01, 0x02 } )] + [DataRow( new byte[] { 0x00, 0x00, 0x00 } )] + public void ReadValue_InvalidBool_ThrowsArgumentException( byte[] value ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value, 0, value.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.Boolean ); + // Assert + act.Should().Throw(); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2B, 0x02, 0x1E }, "12/31/1999 23:59:59 +02:30", DisplayName = "Time with negative offset" )] + [DataRow( new byte[] { 0x00, 0x0B, 0x07, 0xCF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, 0x00, 0x2D, 0x02, 0x1E }, "12/31/1999 23:59:59 -02:30", DisplayName = "Time with positive offset" )] + [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, "01/01/0001 01:01:01 +00:00", DisplayName = "Minimal DateTime" )] + public void ReadValue_DateTimeOffset_ReturnsCorrectResult( byte[] value, string expected ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value, 0, value.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.DateTime ); + // Assert + act.Should().NotThrow().Which.Should().Be( DateTimeOffset.Parse( expected, CultureInfo.InvariantCulture ) ); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, DisplayName = "Invalid second byte" )] + [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, DisplayName = "Invalid offset sign" )] + [DataRow( new byte[] { 0x00, 0x0B, 0x00, 0x01, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x00, 0x2D, 0x00, 0x00 }, DisplayName = "Invalid month" )] + public void ReadValue_InvalidDateTimeOffset_ThrowsArgumentException( byte[] value ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.DateTime ); + // Assert + act.Should().Throw(); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }, int.MinValue, int.MinValue )] + [DataRow( new byte[] { 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF }, int.MinValue, int.MaxValue )] + [DataRow( new byte[] { 0x00, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, int.MaxValue, int.MaxValue )] + public void ReadValue_Range_ReturnsCorrectResult( byte[] value, int lower, int upper ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value, 0, value.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.RangeOfInteger ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( new Models.Range( lower, upper ) ); + } + + + [TestMethod()] + [DataRow( new byte[] { 0x01, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid first byte" )] + [DataRow( new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF }, DisplayName = "Invalid second byte" )] + public void ReadValue_InvalidRange_ShouldThrowArgumentException( byte[] value ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( value ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.RangeOfInteger ); + // Assert + act.Should().Throw(); + } + + [TestMethod()] + [DataRow( new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, 0, int.MaxValue, ResolutionUnit.DotsPerCm )] + [DataRow( new byte[] { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 }, 0, int.MaxValue, ResolutionUnit.DotsPerInch )] + [DataRow( new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerCm )] + [DataRow( new byte[] { 0x00, 0x09, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x03 }, int.MaxValue, int.MaxValue, ResolutionUnit.DotsPerInch )] + public void ReadValue_Resolution_ReturnsCorrectResult( byte[] bytes, int width, int height, ResolutionUnit unit ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( bytes, 0, bytes.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.Resolution ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( new Resolution( width, height, unit ) ); + } + + [TestMethod()] + [DataRow( new byte[] { 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, DisplayName = "Invalid first byte" )] + [DataRow( new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x04 }, DisplayName = "Invalid second byte" )] + public void ReadValue_InvalidResolution_ThrowsArgumentException( byte[] bytes ) + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( bytes, 0, bytes.Length ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, Tag.Resolution ); + // Assert + act.Should().Throw(); + } + + [TestMethod] + public void ReadValue_InvalidTag_ThrowsArgumentException() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new(); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadValue( binaryReader, (Tag)0x01 ); + // Assert + act.Should().Throw(); + } + + [TestMethod] + public void ReadAttribute_OneAttribute_ReturnsCorrectResult() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x16, 0x69, 0x70, 0x70, 0x2D, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6F, 0x6E, 0x73, 0x2D, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, null ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ) ); + } + + [TestMethod] + public void ReadAttribute_SecondSimilarAttribute_ReturnsCorrectResult() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); + using BinaryReader binaryReader = new( memoryStream ); + var previousAttribute = new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 0 ).ToString() ); + // Act + Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, previousAttribute ); + // Assert + act.Should().NotThrow().Which.Should().BeEquivalentTo( new IppAttribute( Tag.Keyword, PrinterAttribute.IppVersionsSupported, new IppVersion( 1, 1 ).ToString() ) ); + } + + [TestMethod] + public void ReadAttribute_AttributeWithoutName_ReturnsCorrectResult() + { + // Arrange + var protocol = new IppProtocol(); + using MemoryStream memoryStream = new( new byte[] { 0x00, 0x00, 0x00, 0x03, 0x31, 0x2E, 0x31 } ); + using BinaryReader binaryReader = new( memoryStream ); + // Act + Func act = () => protocol.ReadAttribute( Tag.Keyword, binaryReader, null ); + // Assert + act.Should().Throw(); } } \ No newline at end of file diff --git a/SharpIppTests/SharpIppClientTests.cs b/SharpIppTests/SharpIppClientTests.cs new file mode 100644 index 0000000..66167ef --- /dev/null +++ b/SharpIppTests/SharpIppClientTests.cs @@ -0,0 +1,860 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq.Protected; +using Moq; +using SharpIpp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using SharpIpp.Protocol; +using SharpIpp.Protocol.Models; +using SharpIppTests.Extensions; +using System.Diagnostics.CodeAnalysis; +using FluentAssertions.Equivalency; +using SharpIpp.Exceptions; +using System.Net.Http; +using SharpIpp.Models; + +namespace SharpIpp.Tests; + +[TestClass] +[ExcludeFromCodeCoverage] +public class SharpIppClientTests +{ + private Mock GetMockOfHttpMessageHandler( HttpStatusCode statusCode = HttpStatusCode.OK ) + { + Mock handlerMock = new( MockBehavior.Strict ); + handlerMock + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + .ReturnsAsync( new HttpResponseMessage() + { + StatusCode = statusCode, + Content = new ByteArrayContent( Array.Empty() ), + } ) + .Verifiable(); + return handlerMock; + } + + private Mock GetMockOfIppProtocol() + { + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( new IppResponseMessage + { + RequestId = 123, + Version = IppVersion.V1_1, + StatusCode = IppStatusCode.SuccessfulOk + } ); + return protocol; + } + + [TestMethod()] + public async Task CreateJobAsync_CreateJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.CreateJob, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task CancelJobAsync_CancelJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.CancelJobAsync( new Models.CancelJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 234 + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.CancelJob, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 234 ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task HoldJobAsync_HoldJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.HoldJobAsync( new Models.HoldJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 234 + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.HoldJob, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 234 ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task PausePrinterAsync_PausePrinterRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.PausePrinterAsync( new Models.PausePrinterRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.PausePrinter, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task ReleaseJobAsync_ReleaseJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.ReleaseJobAsync( new Models.ReleaseJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 234 + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.ReleaseJob, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 234 ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task PrintJobAsync_PrintJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + using MemoryStream memoryStream = new(); + // Act + await client.PrintJobAsync( new Models.PrintJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + Document = memoryStream + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.PrintJob, + RequestId = 123, + Version = IppVersion.V1_1, + Document = memoryStream + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, x => x.Excluding( ( IMemberInfo x ) => x.Path == "Document.ReadTimeout" || x.Path == "Document.WriteTimeout" ), "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task ResumePrinterAsync_ResumePrinterRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.ResumePrinterAsync( new Models.ResumePrinterRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.ResumePrinter, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task ValidateJobAsync_ValidateJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + using MemoryStream memoryStream = new(); + // Act + await client.ValidateJobAsync( new Models.ValidateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + Document = memoryStream + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.ValidateJob, + RequestId = 123, + Version = IppVersion.V1_1, + Document = memoryStream + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task PrintUriAsync_PrintUriRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + var uri = new Uri( "http://test.com/document.pdf" ); + // Act + await client.PrintUriAsync( new Models.PrintUriRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + DocumentUri = uri + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.PrintUri, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Uri, JobAttribute.DocumentUri, uri.AbsoluteUri ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task SendDocumentAsync_SendDocumentRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + using MemoryStream memoryStream = new(); + // Act + await client.SendDocumentAsync( new Models.SendDocumentRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 456, + Document = memoryStream + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.SendDocument, + RequestId = 123, + Version = IppVersion.V1_1, + Document = memoryStream + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 456 ), + new IppAttribute( Tag.Boolean, JobAttribute.LastDocument, false ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task SendUriAsync_SendUriRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + var uri = new Uri( "http://test.com/document.pdf" ); + // Act + await client.SendUriAsync( new Models.SendUriRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 456, + DocumentUri = uri + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.SendUri, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Uri, JobAttribute.DocumentUri, uri.AbsoluteUri ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 456 ), + new IppAttribute( Tag.Boolean, JobAttribute.LastDocument, false ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task GetJobAttributesAsync_GetJobAttributesRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.GetJobAttributesAsync( new Models.GetJobAttributesRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 456 + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.GetJobAttributes, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 456 ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task GetJobsAsync_GetJobsRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.GetJobsAsync( new Models.GetJobsRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.GetJobs, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task GetPrinterAttributesAsync_GetPrinterAttributesRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.GetPrinterAttributesAsync( new Models.GetPrinterAttributesRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.GetPrinterAttributes, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task PurgeJobsAsync_PurgeJobsRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.PurgeJobsAsync( new Models.PurgeJobsRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.PurgeJobs, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task RestartJobAsync_RestartJobRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.RestartJobAsync( new Models.RestartJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user", + JobId = 456 + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.RestartJob, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ), + new IppAttribute( Tag.Integer, JobAttribute.JobId, 456 ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task GetCUPSPrintersAsync_CUPSGetPrintersRequest_ShouldBeMapped() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + await client.GetCUPSPrintersAsync( new Models.CUPSGetPrintersRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + IppRequestMessage rawRequestMessage = new() + { + IppOperation = IppOperation.GetCUPSPrinters, + RequestId = 123, + Version = IppVersion.V1_1 + }; + rawRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.NameWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "http://127.0.0.1:631/" ) + } ); + protocol.Verify( x => x.WriteIppRequestAsync( + It.Is( x => x.VerifyAssertionScope( _ => x.Should().BeEquivalentTo( rawRequestMessage, "" ) ) ), + It.IsAny(), + It.IsAny() ) ); + } + + [TestMethod()] + public async Task CreateJobAsync_ResponseWithValidIppCodeAndInvalidHttpState_ShouldThrowException() + { + // Arrange + HttpClient httpClient = new( GetMockOfHttpMessageHandler( HttpStatusCode.BadRequest ).Object ); + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( new IppResponseMessage + { + RequestId = 123, + Version = IppVersion.V1_1, + StatusCode = IppStatusCode.SuccessfulOk + } ); + SharpIppClient client = new( httpClient, protocol.Object ); + // Act + Func> act = async () => await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod()] + public async Task CreateJobAsync_ValidResponseWithPlausibleState_ShouldThrowException() + { + // Arrange + HttpClient httpClient = new( GetMockOfHttpMessageHandler( HttpStatusCode.Unauthorized ).Object ); + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( new IppResponseMessage + { + RequestId = 123, + Version = IppVersion.V1_1, + StatusCode = IppStatusCode.SuccessfulOk + } ); + SharpIppClient client = new( httpClient, protocol.Object ); + // Act + Func> act = async () => await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod()] + public async Task CreateJobAsync_EmptyResponseWithValidHttpState_ShouldThrowException() + { + // Arrange + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( (IIppResponseMessage?)null ); + SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + Func> act = async () => await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod()] + public async Task CreateJobAsync_EmptyResponseWithPlausibleHttpState_ShouldThrowException() + { + // Arrange + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( (IIppResponseMessage?)null ); + SharpIppClient client = new( new( GetMockOfHttpMessageHandler( HttpStatusCode.Unauthorized ).Object ), protocol.Object ); + // Act + Func> act = async () => await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod()] + public async Task CreateJobAsync_ResponseWithInvalidIppCodeAndValidHttpState_ShouldThrowException() + { + // Arrange + Mock protocol = new(); + protocol.Setup( x => x.ReadIppResponseAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( new IppResponseMessage + { + RequestId = 123, + Version = IppVersion.V1_1, + StatusCode = IppStatusCode.ServerErrorBusy + } ); + SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act + Func> act = async () => await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "http://127.0.0.1:631" ), + RequestingUserName = "test-user" + } ); + // Assert + await act.Should().ThrowAsync(); + } + + [TestMethod] + public void Constructor_Default_InstanceShouldBeCreated() + { + // Arrange & Act + using SharpIppClient client = new(); + // Assert + client.Should().NotBeNull(); + } + + [TestMethod] + public void Constructor_HttpClient_InstanceShouldBeCreated() + { + // Arrange & Act + using SharpIppClient client = new( new HttpClient() ); + // Assert + client.Should().NotBeNull(); + } + + [TestMethod] + public void Constructor_HttpClientAndIppProtocol_InstanceShouldBeCreated() + { + // Arrange & Act + using SharpIppClient client = new( new HttpClient(), new IppProtocol() ); + // Assert + client.Should().NotBeNull(); + } + + [TestMethod()] + public void Construct_InvalidData_ShouldThrowException() + { + // Arrange + using SharpIppClient client = new(); + var message = new Mock(); + //Act + Func act = () => client.Construct( message.Object ); + // Assert + act.Should().Throw(); + } + + [TestMethod()] + public async Task CreateJobAsync_Null_ShouldThrowException() + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + using SharpIppClient client = new( new( GetMockOfHttpMessageHandler().Object ), protocol.Object ); + // Act +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + Func> act = async () => await client.CreateJobAsync( null ); +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + // Assert + await act.Should().ThrowAsync(); + } + + [DataTestMethod] + [DataRow( "http://127.0.0.1:631", "http://127.0.0.1:631" )] + [DataRow( "https://127.0.0.1:631", "https://127.0.0.1:631" )] + [DataRow( "ipp://127.0.0.1:631", "http://127.0.0.1:631" )] + [DataRow( "ipps://127.0.0.1:631", "https://127.0.0.1:631" )] + [DataRow( "ipp://127.0.0.1", "http://127.0.0.1:631" )] + public async Task CreateJobAsync_PrinterUri_ShouldBeUpdated(string printerUri, string expected ) + { + // Arrange + Mock protocol = GetMockOfIppProtocol(); + Mock messageHandler = GetMockOfHttpMessageHandler(); + using SharpIppClient client = new( new( messageHandler.Object ), protocol.Object ); + // Act + await client.CreateJobAsync( new Models.CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( printerUri ), + RequestingUserName = "test-user" + } ); + // Assert + messageHandler + .Protected() + .Verify>( + "SendAsync", + Times.Once(), + ItExpr.Is(x => x.VerifyAssertionScope(_ => x.RequestUri.Should().BeEquivalentTo(new Uri(expected), "" ))), + ItExpr.IsAny() ); + } +} \ No newline at end of file diff --git a/SharpIppTests/SharpIppServerTests.cs b/SharpIppTests/SharpIppServerTests.cs new file mode 100644 index 0000000..821bb7b --- /dev/null +++ b/SharpIppTests/SharpIppServerTests.cs @@ -0,0 +1,93 @@ +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; +using Moq; +using SharpIpp.Models; +using SharpIpp.Protocol; +using SharpIpp.Protocol.Models; +using System.Diagnostics.CodeAnalysis; + +namespace SharpIpp.Tests; + +[TestClass] +[ExcludeFromCodeCoverage] +public class SharpIppServerTests +{ + [TestMethod] + public async Task ReceiveRawRequestAsync_Stream_ShouldBeWritten() + { + // Arrange + Mock ippProtocol = new(); + IppRequestMessage ippRequestMessage = new(); + ippProtocol.Setup( x => x.ReadIppRequestAsync( It.IsAny(), It.IsAny() ) ).ReturnsAsync( ippRequestMessage ); + SharpIppServer server = new( ippProtocol.Object ); + MemoryStream memoryStream = new(); + // Act + Func> act = async () => await server.ReceiveRawRequestAsync( memoryStream ); + // Assert + (await act.Should().NotThrowAsync()).Which.Should().Be( ippRequestMessage ); + } + + [TestMethod] + public async Task ReceiveRequestAsync_CancelJob_ShouldBeMapped() + { + // Arrange + SharpIppServer server = new( Mock.Of() ); + IppRequestMessage ippRequestMessage = new() + { + IppOperation = IppOperation.CancelJob, + Version = IppVersion.V1_1, + RequestId = 123, + }; + ippRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "ipp://127.0.0.1:631/" ), + new IppAttribute( Tag.Uri, JobAttribute.JobId, 123 ), + new IppAttribute( Tag.TextWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ) + } ); + // Act + Func> act = async () => await server.ReceiveRequestAsync( ippRequestMessage ); + // Assert + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( new CancelJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + JobId = 123, + PrinterUri = new Uri( "ipp://127.0.0.1:631/" ), + RequestingUserName = "test-user" + } ); + } + + [TestMethod] + public async Task ReceiveRequestAsync_CreateJob_ShouldBeMapped() + { + // Arrange + SharpIppServer server = new( Mock.Of() ); + IppRequestMessage ippRequestMessage = new() + { + IppOperation = IppOperation.CreateJob, + Version = IppVersion.V1_1, + RequestId = 123, + }; + ippRequestMessage.OperationAttributes.AddRange( new[] + { + new IppAttribute( Tag.Charset, JobAttribute.AttributesCharset, "utf-8" ), + new IppAttribute( Tag.NaturalLanguage, JobAttribute.AttributesNaturalLanguage, "en" ), + new IppAttribute( Tag.Uri, JobAttribute.PrinterUri, "ipp://127.0.0.1:631/" ), + new IppAttribute( Tag.Uri, JobAttribute.JobId, 123 ), + new IppAttribute( Tag.TextWithoutLanguage, JobAttribute.RequestingUserName, "test-user" ) + } ); + // Act + Func> act = async () => await server.ReceiveRequestAsync( ippRequestMessage ); + // Assert + (await act.Should().NotThrowAsync()).Which.Should().BeEquivalentTo( new CreateJobRequest + { + RequestId = 123, + Version = IppVersion.V1_1, + PrinterUri = new Uri( "ipp://127.0.0.1:631/" ), + RequestingUserName = "test-user", + + NewJobAttributes = new() + } ); + } +} \ No newline at end of file diff --git a/SharpIppTests/SharpIppTests.csproj b/SharpIppTests/SharpIppTests.csproj index f63c5da..2767647 100644 --- a/SharpIppTests/SharpIppTests.csproj +++ b/SharpIppTests/SharpIppTests.csproj @@ -11,7 +11,8 @@ - + +