From fa31194d9a494e22dc9d520c9674861aacad403c Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 10:18:23 -0400 Subject: [PATCH 01/16] Migrate all assertions to Shouldly syntax --- QRCoderTests/ArtQRCodeRendererTests.cs | 4 +- .../PayloadGeneratorTests/BezahlCodeTests.cs | 84 ++++++------- .../PayloadGeneratorTests/GirocodeTests.cs | 40 +++---- .../MoneroTransactionTests.cs | 8 +- .../RussiaPaymentOrderTests.cs | 26 ++--- .../ShadowSocksConfigTests.cs | 8 +- .../PayloadGeneratorTests/SwissQrCodeTests.cs | 110 +++++++++--------- 7 files changed, 140 insertions(+), 140 deletions(-) diff --git a/QRCoderTests/ArtQRCodeRendererTests.cs b/QRCoderTests/ArtQRCodeRendererTests.cs index 2cc91bec..439403df 100644 --- a/QRCoderTests/ArtQRCodeRendererTests.cs +++ b/QRCoderTests/ArtQRCodeRendererTests.cs @@ -69,8 +69,8 @@ public void should_throw_pixelfactor_oor_exception() var aCode = new ArtQRCode(data); var exception = Record.Exception(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The parameter pixelSize must be between 0 and 1. (0-100%)"); } diff --git a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs index 7d181565..bcebbc7b 100644 --- a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs @@ -331,8 +331,8 @@ public void bezahlcode_generator_should_throw_wrong_contact_constructor_exceptio #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); } @@ -346,8 +346,8 @@ public void bezahlcode_generator_should_throw_wrong_contact_v2_constructor_excep var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban: iban, bic: bic)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); } @@ -362,8 +362,8 @@ public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_exceptio var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, account: account, bnc: bnc, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); } @@ -384,8 +384,8 @@ public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_periodic #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); } @@ -400,8 +400,8 @@ public void bezahlcode_generator_should_throw_wrong_sepa_constructor_exception() #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); } @@ -423,8 +423,8 @@ public void bezahlcode_generator_should_throw_wrong_sepa_constructor_periodic_ex var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); } @@ -440,8 +440,8 @@ public void bezahlcode_generator_should_throw_name_too_long_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); } @@ -458,8 +458,8 @@ public void bezahlcode_generator_should_throw_reason_too_long_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount, reason: reason)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Reasons texts have to be shorter than 141 chars."); } @@ -475,8 +475,8 @@ public void bezahlcode_generator_should_throw_invalid_account_exception() #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The account entered isn't valid."); } @@ -491,8 +491,8 @@ public void bezahlcode_generator_should_throw_invalid_bnc_exception() #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The bnc entered isn't valid."); } @@ -508,8 +508,8 @@ public void bezahlcode_generator_should_throw_invalid_postingkey_exception() #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("PostingKey must be within 0 and 99."); } @@ -524,8 +524,8 @@ public void bezahlcode_generator_should_throw_invalid_iban_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -541,8 +541,8 @@ public void bezahlcode_generator_should_throw_invalid_bic_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The BIC entered isn't valid."); } @@ -563,8 +563,8 @@ public void bezahlcode_generator_should_throw_separeference_too_long_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("SEPA reference texts have to be shorter than 36 chars."); } @@ -585,8 +585,8 @@ public void bezahlcode_generator_should_throw_invalid_creditorid_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The creditorId entered isn't valid."); } @@ -606,8 +606,8 @@ public void bezahlcode_generator_should_throw_invalid_mandateid_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The mandateId entered isn't valid."); } @@ -621,8 +621,8 @@ public void bezahlcode_generator_should_throw_amount_too_much_digits_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); } @@ -638,8 +638,8 @@ public void bezahlcode_generator_should_throw_amount_too_big_exception() var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -659,8 +659,8 @@ public void bezahlcode_generator_should_throw_invalid_executiondate_exception() #pragma warning disable CS0612 var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Execution date must be today or in future."); } @@ -682,8 +682,8 @@ public void bezahlcode_generator_should_throw_invalid_periodictimeunit_exception var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); } @@ -705,8 +705,8 @@ public void bezahlcode_generator_should_throw_invalid_periodictimeunitrotation_e var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); } } diff --git a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs index 63a8f22d..f7df3a60 100644 --- a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs @@ -139,8 +139,8 @@ public void girocode_generator_should_throw_iban_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -160,8 +160,8 @@ public void girocode_generator_should_throw_bic_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The BIC entered isn't valid."); } @@ -181,8 +181,8 @@ public void girocode_generator_should_throw_name_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); } @@ -202,8 +202,8 @@ public void girocode_generator_should_throw_amount_decimals_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); } @@ -222,8 +222,8 @@ public void girocode_generator_should_throw_amount_min_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -244,8 +244,8 @@ public void girocode_generator_should_throw_amount_max_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -265,8 +265,8 @@ public void girocode_generator_should_throw_purpose_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Purpose of credit transfer can only have 4 chars at maximum."); } @@ -285,8 +285,8 @@ public void girocode_generator_should_throw_remittance_unstructured_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Unstructured reference texts have to be shorter than 141 chars."); } @@ -305,8 +305,8 @@ public void girocode_generator_should_throw_remittance_structured_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Structured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Structured reference texts have to be shorter than 36 chars."); } @@ -325,8 +325,8 @@ public void girocode_generator_should_throw_usermessage_exception() var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Message to the Girocode-User reader texts have to be shorter than 71 chars."); } diff --git a/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs b/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs index 9ac4bc2e..d7077b00 100644 --- a/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs @@ -67,8 +67,8 @@ public void monero_generator_should_throw_wrong_amount_exception() var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address, amount)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'txAmount' must be greater than 0."); } @@ -80,8 +80,8 @@ public void monero_generator_should_throw_no_address_exception() var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The address is mandatory and has to be set."); } } diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs index 2533ad59..5308c210 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs @@ -35,10 +35,10 @@ public void russiapayment_generator_can_generate_payload_encoding_win1251() byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 49, 124, 78, 97, 109, 101, 61, 206, 206, 206, 32, 171, 210, 240, 232, 32, 234, 232, 242, 224, 187, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 206, 192, 206, 32, 34, 193, 192, 205, 202, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; var payloadBytes = generator.ToBytes(); - Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); + targetBytes.Length.ShouldBe(payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); for (int i = 0; i < targetBytes.Length; i++) { - Assert.True(targetBytes[i] == payloadBytes[i], + targetBytes[i].ShouldBe(payloadBytes[i], $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." ); } @@ -57,10 +57,10 @@ public void russiapayment_generator_can_generate_payload_encoding_koi8() byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 51, 124, 78, 97, 109, 101, 61, 239, 239, 239, 32, 60, 244, 210, 201, 32, 203, 201, 212, 193, 62, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 239, 225, 239, 32, 34, 226, 225, 238, 235, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; var payloadBytes = generator.ToBytes(); - Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); + targetBytes.Length.ShouldBe(payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); for (int i = 0; i < targetBytes.Length; i++) { - Assert.True(targetBytes[i] == payloadBytes[i], + targetBytes[i].ShouldBe(payloadBytes[i], $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." ); } @@ -92,8 +92,8 @@ public void russiapayment_generator_should_throw_no_separator_exception() var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); var exception = Record.Exception(() => generator.ToString()); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("No valid separator found."); } @@ -110,8 +110,8 @@ public void russiapayment_generator_should_throw_data_too_long_exception() var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); var exception = Record.Exception(() => generator.ToString()); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldStartWith("Data too long"); } @@ -135,7 +135,7 @@ public void russiapayment_generator_should_throw_no_data_too_long_exception() // Should throw no exception as the 300 byte limit applies only to the mandatory fields // See https://github.com/Shane32/QRCoder/issues/392 var exception = Record.Exception(() => generator.ToString()); - Assert.Null(exception); + exception.ShouldBeNull(); } [Fact] @@ -148,8 +148,8 @@ public void russiapayment_generator_should_throw_must_not_be_null_exception() var correspAcc = "30101810400000000225"; var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe($"The input for 'PersonalAcc' must not be null."); } @@ -163,8 +163,8 @@ public void russiapayment_generator_should_throw_unmatched_pattern_exception() var correspAcc = "30101810400000000225"; var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The input for 'BIC' (abcd) doesn't match the pattern ^\\d{9}$"); } diff --git a/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs b/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs index 2a7e1e04..fcdcf82e 100644 --- a/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs @@ -46,8 +46,8 @@ public void shadowsocks_generator_should_throw_portrange_low_exception() var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); } @@ -62,8 +62,8 @@ public void shadowsocks_generator_should_throw_portrange_high_exception() var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); } diff --git a/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs b/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs index dfa1c89a..85b7d078 100644 --- a/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs @@ -19,8 +19,8 @@ public void swissqrcode_generator_should_throw_reference_not_allowed() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Reference is only allowed when referenceType not equals \"NON\""); } @@ -32,8 +32,8 @@ public void swissqrcode_generator_should_throw_missing_reftexttype() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("You have to set an ReferenceTextType when using the reference text."); } @@ -47,8 +47,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_too_long() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-references have to be shorter than 28 chars."); } @@ -62,8 +62,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_wrong_char() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-reference must exist out of digits only."); } @@ -77,8 +77,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_checksum_invalid() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-references is invalid. Checksum error."); } @@ -92,8 +92,8 @@ public void swissqrcode_generator_should_throw_iso11649_ref_too_long() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Creditor references (ISO 11649) have to be shorter than 26 chars."); } @@ -105,8 +105,8 @@ public void swissqrcode_generator_should_throw_unstructured_msg_too_long() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.AdditionalInformation(unstructuredMessage, billInformation)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Unstructured message and bill information must be shorter than 141 chars in total/combined."); } @@ -172,8 +172,8 @@ public void swissqrcode_generator_should_throw_invalid_iban() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -185,8 +185,8 @@ public void swissqrcode_generator_should_throw_invalid_qriban() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The QR-IBAN entered isn't valid."); } @@ -198,8 +198,8 @@ public void swissqrcode_generator_should_throw_ivalid_iban_country() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN must start with \"CH\" or \"LI\"."); } @@ -250,8 +250,8 @@ public void swissqrcode_generator_should_throw_name_empty() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Name must not be empty."); } @@ -268,8 +268,8 @@ public void swissqrcode_generator_should_throw_name_too_long() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Name must be shorter than 71 chars."); } @@ -286,8 +286,8 @@ public void swissqrcode_generator_should_throw_street_too_long() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Street must be shorter than 71 chars."); } @@ -304,8 +304,8 @@ public void swissqrcode_generator_should_throw_street_with_illegal_char() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe(@"Street must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); } @@ -322,8 +322,8 @@ public void swissqrcode_generator_should_throw_housenumber_too_long() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("House number must be shorter than 17 chars."); } @@ -340,8 +340,8 @@ public void swissqrcode_generator_should_throw_zip_empty() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Zip code must not be empty."); } @@ -357,8 +357,8 @@ public void swissqrcode_generator_should_throw_zip_too_long() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Zip code must be shorter than 17 chars."); } @@ -375,8 +375,8 @@ public void swissqrcode_generator_should_throw_zip_has_illegal_char() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe(@"Zip code must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); } @@ -393,8 +393,8 @@ public void swissqrcode_generator_should_throw_city_empty() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("City must not be empty."); } @@ -411,8 +411,8 @@ public void swissqrcode_generator_should_throw_city_too_long() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("City name must be shorter than 36 chars."); } @@ -429,8 +429,8 @@ public void swissqrcode_generator_should_throw_wrong_countrycode() var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); } @@ -552,8 +552,8 @@ public void swissqrcode_generator_should_throw_amount_too_big() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount (including decimals) must be shorter than 13 places."); } @@ -570,8 +570,8 @@ public void swissqrcode_generator_should_throw_incompatible_reftype() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); } @@ -589,8 +589,8 @@ public void swissqrcode_generator_should_throw_alt1_too_long() var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Alternative procedure information block 1 must be shorter than 101 chars."); } @@ -608,8 +608,8 @@ public void swissqrcode_generator_should_throw_alt2_too_long() var alt2 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1, alt2)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Alternative procedure information block 2 must be shorter than 101 chars."); } @@ -623,26 +623,26 @@ public void swissqrcode_generator_should_validate_two_lettercodes() // Should work, as DE is a valid country code string country = "DE"; var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); + exception.ShouldBeNull(); // Should work, as de is a valid country code and case should be ignored country = "de"; exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); + exception.ShouldBeNull(); // Should work, as XK is is defined as special case (not officially ISO-3166-1,but used in the wild) // See https://en.wikipedia.org/wiki/XK_(user_assigned_code) and https://github.com/Shane32/QRCoder/issues/420 country = "XK"; exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); + exception.ShouldBeNull(); // Should throw exception, as ZZ isn't a valid country code country = "ZZ"; exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.NotNull(exception); - Assert.IsType(exception); + exception.ShouldNotBeNull(); + exception.ShouldBeOfType(); exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); } } From 98014cee1d53128be6937486354f660cfa24d70a Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 10:27:36 -0400 Subject: [PATCH 02/16] Update RussiaPaymentOrderTests to use approved output for encoding tests --- .gitignore | 1 + .../RussiaPaymentOrderTests.cs | 24 ++----------------- ...enerate_payload_encoding_koi8.approved.txt | 1 + ...rate_payload_encoding_win1251.approved.txt | 1 + 4 files changed, 5 insertions(+), 22 deletions(-) create mode 100644 QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt create mode 100644 QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt diff --git a/.gitignore b/.gitignore index 807fbc9e..39720ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -221,4 +221,5 @@ pip-log.txt QRCoder/PortabilityAnalysis.html # Unaccepted approval files +QRCoderTests/*.received.* *.received.txt diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs index 5308c210..38d7937c 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs @@ -31,17 +31,7 @@ public void russiapayment_generator_can_generate_payload_encoding_win1251() var name = "ООО «Три кита»"; var correspAcc = "30101810965770000413"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.windows_1251); - - byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 49, 124, 78, 97, 109, 101, 61, 206, 206, 206, 32, 171, 210, 240, 232, 32, 234, 232, 242, 224, 187, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 206, 192, 206, 32, 34, 193, 192, 205, 202, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; - var payloadBytes = generator.ToBytes(); - - targetBytes.Length.ShouldBe(payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); - for (int i = 0; i < targetBytes.Length; i++) - { - targetBytes[i].ShouldBe(payloadBytes[i], - $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." - ); - } + generator.ToString().ShouldMatchApproved(x => x.NoDiff()); } [Fact] @@ -53,17 +43,7 @@ public void russiapayment_generator_can_generate_payload_encoding_koi8() var name = "ООО «Три кита»"; var correspAcc = "30101810965770000413"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.koi8_r); - - byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 51, 124, 78, 97, 109, 101, 61, 239, 239, 239, 32, 60, 244, 210, 201, 32, 203, 201, 212, 193, 62, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 239, 225, 239, 32, 34, 226, 225, 238, 235, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; - var payloadBytes = generator.ToBytes(); - - targetBytes.Length.ShouldBe(payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); - for (int i = 0; i < targetBytes.Length; i++) - { - targetBytes[i].ShouldBe(payloadBytes[i], - $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." - ); - } + generator.ToString().ShouldMatchApproved(x => x.NoDiff()); } [Fact] diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt new file mode 100644 index 00000000..32d355fa --- /dev/null +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt @@ -0,0 +1 @@ +ST00013|Name=ООО <Три кита>|PersonalAcc=40702810138250123017|BankName=ОАО "БАНК"|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt new file mode 100644 index 00000000..187e8650 --- /dev/null +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt @@ -0,0 +1 @@ +ST00011|Name=ООО «Три кита»|PersonalAcc=40702810138250123017|BankName=ОАО "БАНК"|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file From 89bb86a47e6b8e28a715a58517f33151c68f2570 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 11:15:25 -0400 Subject: [PATCH 03/16] test --- ...render_pngbyte_qrcode_blackwhite.approved.bmp | Bin 0 -> 3944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0c40703e2ff78809879cef89cc131b61305f122e GIT binary patch literal 3944 zcmdT{F>b>!45SJODAYkiylC;_#VF7p6df{jE6`{3V|{1K}F%(@h z%A%4&(s|OgS6h3$E$Z5c1B6&Or!JYkRBZp**Rdy!d6ZIfjlH)0`+NqUTyv5YE9R`t zhWB1})Gp!!zJNEV8OJu(wuP4(Bk%~~{OzfKwY3^wn1`gC78z|@ZU4LN9W@QkqL1{} zqh~Va7*|^tHR1ik>tQAz)F!wDob12MA@Vlf#-6iV@5zPw-q358^SYsH0b+oN0hNR~ Date: Thu, 2 Oct 2025 11:19:32 -0400 Subject: [PATCH 04/16] test --- ...render_pngbyte_qrcode_blackwhite.approved.bmp | Bin 3944 -> 0 bytes ...render_pngbyte_qrcode_blackwhite.approved.gif | Bin 0 -> 1370 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.gif diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.bmp deleted file mode 100644 index 0c40703e2ff78809879cef89cc131b61305f122e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3944 zcmdT{F>b>!45SJODAYkiylC;_#VF7p6df{jE6`{3V|{1K}F%(@h z%A%4&(s|OgS6h3$E$Z5c1B6&Or!JYkRBZp**Rdy!d6ZIfjlH)0`+NqUTyv5YE9R`t zhWB1})Gp!!zJNEV8OJu(wuP4(Bk%~~{OzfKwY3^wn1`gC78z|@ZU4LN9W@QkqL1{} zqh~Va7*|^tHR1ik>tQAz)F!wDob12MA@Vlf#-6iV@5zPw-q358^SYsH0b+oN0hNR~H$)6}{LIlar)4-la^Z8nu(xcD>4T0K>in3@j zeNM6B%u3Z^P@*lZYGny?CR&DLcb@baQPkG4HNo;UCpYd{vQ5o~bxOAt*Q#!x(IQN* z>R-bE{Yqo1O0hu4Qr8|nY2mFhuM~<+Mb}j_G{_t<`YS5N5;coJ ziZJb5l~!#(y~(>S)ZD@G{$BmKIB?vumGk}m4SDL}(V0W19iqB$>VlVFg+9gfXXD>} zWFH@-y!jvK)Av}kl>q0Srp1`O4aZ zuC^$XZb1RLb5JI==Icy5!0;0dnZAy?OT!Nj%g=+rQsi#H%qVO@#k>NG(7MYcWC6wy zb5wB0!UBA8#M|gQa6<7~Os@#!8RZCb4C|;&=WcT5==CP zIH?4(H@&`GvBcw=B<{$;X!MiC=N5glQBF5y^G7@bbu>oNWYq12Mz=gwN7pbNl~E5h zMX}Z(J$+8qp>Ty%S9XFNGc*yG95h%+hyRTg$0LF5wOBbv^VP{SD=pGhYSoOD&})%h zwm|-x)o;kOSWVS7Emh;z%5#xQcQ$e(y|Gv6k|0r?)F)VvEvxqN6EXi&VxNpxxJP5 zyqM#UN2ZlwL?^B!;5lQ?nNOEd&DRNDAqMzAXP+qAV9qX0Z)uUY{a5LikG4A8q=~ND zXs$CX+2-p^*7$?+nB7(870P~8SrgIjR5!DcW%+G>otE2eh3nqew~g&JCd)8E9=dOJ z0|y&SN$(sRUa%1kw`pS&7ku%1DgXBxPHj#7R`JFQXWVZy=@s|tuyKypaf4BQ_;R{u z4wcBw1@)}+&o#H0_1jf16nBzoo|X5mbsrt;yj}l&-L~CC{X$ofr|EZQ5!d%?wN39> za^EALKKpnH1{CP$wJ)9eNv-F(6w)t5N^_L+XB`mfJt-gelL_P>AN z3*P#g2e2Z;wlN^LKOD~zBCE%+-Ji0yQhNnW(1H$Lp>?|+dQ9{e(Q zE3qB%gGq#({IXThsV#AJ?+K$A$4JI9n(>TiOrsjt$i_Ch@r`hd cqa5c*$2!{aj(E(Y9{0${KKk*GAQ%7uJIyM;H~;_u literal 0 HcmV?d00001 From 8d2323e4e9e82f52048d006dbcfc68570f9bb84b Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 12:48:43 -0400 Subject: [PATCH 05/16] test --- ...der_pngbyte_qrcode_blackwhite.approved.gif | Bin 1370 -> 1395 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_blackwhite.approved.gif index 18f89c2a60614bc96acd69ccf45b80341819976b..ea6ee4617ec964db2ed52da614f099a1e6f19b05 100644 GIT binary patch literal 1395 zcmV-(1&sPfNk%w1VWj}20Du4h00030|NkNW6iY{WAX869R3Jx5Mj%CQV{c?-asVs< z0001`0Hpu`0{@JUsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~}=lag~{tpZa zhs2`sh)gP%%%<}RjY_9fD&UIEYPVc&Li^2zu@f+&i%xf0>UMj4g#+Ye@!5^{uJU~Q z!TjfE00==C(1*x|$VYca<`^eoxX8FiiPsqTs2LeCdFiIOsbMIW*HL-Y5$#LtMYtmcz%UQV*{BSHgN;^Cl%iBzm4842`Id`4rpp9F-ODx*y9D1$0 z*1+xSO%86k{#Kriwmsi14$D4%fAX1+e{YRJI$`hBWJ=KwUOQp~6*Qz*u;DagvjXbF z#;{(-ME~(N>RE`e;fCA-;!vY6v2t}Ov!+^yWqYOsI+0Y@ zvNy&0L`QdSTC-2lj)iLXmDj6qquDZS?`mMg0s&KV%8D^T$W+@VPKhBdGq4tpRaN(u zvNXvaG-JlQx6abXiu;OI{W`2F#}qk}P^xfkU6xpJN6qQ`uGHPb^#*Sp`M7Z1w3+)2 z9!~k{;?tc=w>{!IaqEPeXO&(Bb!g<_fov~7ev5h@4hm}#?`-LCwqUV>Dlw; zkNbGY(D$Eg2nsk9Q}vzIhh1-3MqPshdIup*Y}NM}cY^6hnlgVSl^2I0 zCbl04gRSUYfty+Q0*iYMHX(MIQRsq z>SmC74hrdvq|wM*43Bo1Dvz*fTB@TUa;oC3LxLI|t3~N5DX;DfS>|XYHd*MflK&Pv ztBy$u`|GiEmIf@8X75CKyz+Z3_nEQBA(w2c!-`gX$-s5aytAJ$tGX`~zDg`` zfY3&9G{T-`x?a;HcN;L%F(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcADYGc5{V8u~ zE9-LH{z6SR+zj8{FK`{>i_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk`TtrDww&Ce z8ZP6HAMQBdXxd9|)?@2DFXRZT9C7A$*F36`of8V$=b>|svFo|BeyHvy+gz*fU-MqN z)_cPqe7kSMsd|R6DsNNp&MMAt*leTzG3CHdUj6p-8Z4;L(Q|*g^_61p^ULG!>YDf2 zd)Rg6;ImKh`tLg(&GliY?>z3ZD-FQ^iEnuAd!F3#hd_PpZ+8XsA9tE}Kn2pOaApJA z_YzpS`@M>R^}C$=Hn=eDx$Sl&L>|$4N3v`A4Rj{t(bNz(LJu0wejqfS2rbpU8MbhO zD8yi}U_iFiU1oXHqF(v5!@mGdrg-w(T&~EL#1Jk~a`fw-%QQnpCOR7McUQ#W>Z;bn z*2QOxWGtf@&xpn}s&S2MY@-|B2*)_eagKDXqaE*v$2{tBk9_Q-AO8r*K#HIM06Q!B B)u#Xe literal 1370 zcmV-g1*Q5&Nk%w1VWj}20Du4h|Ns90001li0001`0Hpu`0sq4KFm<{8!6+xK_TsEJ z@BRZraU@IgL{oKTTla-yd8TXo#&dn=d;bRv3Wvm^@rX<+m&~T~35`mnfvWY2&1$s= zZ1?)LM!bft`Mg!3*XwZA9e|I)WwxE0y6fo$a-WaD&p{Ai&rr=!Pp(dk@eP77Q87=_ zZt<@X^YJ0Fk_|BvgAnd+W3=LQBz0o7Fwqq7q7`HoGM3a*qn7iQWOuMvb28(%p%^w) zHaPHAcUj`tIl0s^uKJ7t+jcrv7<7~Qvl=&TL7SIb+?z7JZT#9SJAND7RXx1Er1Rds zo?5{H$)6}{LIlar)4-la^Z8nu(xcD>4T0K>in3@j zeNM6B%u3Z^P@*lZYGny?CR&DLcb@baQPkG4HNo;UCpYd{vQ5o~bxOAt*Q#!x(IQN* z>R-bE{Yqo1O0hu4Qr8|nY2mFhuM~<+Mb}j_G{_t<`YS5N5;coJ ziZJb5l~!#(y~(>S)ZD@G{$BmKIB?vumGk}m4SDL}(V0W19iqB$>VlVFg+9gfXXD>} zWFH@-y!jvK)Av}kl>q0Srp1`O4aZ zuC^$XZb1RLb5JI==Icy5!0;0dnZAy?OT!Nj%g=+rQsi#H%qVO@#k>NG(7MYcWC6wy zb5wB0!UBA8#M|gQa6<7~Os@#!8RZCb4C|;&=WcT5==CP zIH?4(H@&`GvBcw=B<{$;X!MiC=N5glQBF5y^G7@bbu>oNWYq12Mz=gwN7pbNl~E5h zMX}Z(J$+8qp>Ty%S9XFNGc*yG95h%+hyRTg$0LF5wOBbv^VP{SD=pGhYSoOD&})%h zwm|-x)o;kOSWVS7Emh;z%5#xQcQ$e(y|Gv6k|0r?)F)VvEvxqN6EXi&VxNpxxJP5 zyqM#UN2ZlwL?^B!;5lQ?nNOEd&DRNDAqMzAXP+qAV9qX0Z)uUY{a5LikG4A8q=~ND zXs$CX+2-p^*7$?+nB7(870P~8SrgIjR5!DcW%+G>otE2eh3nqew~g&JCd)8E9=dOJ z0|y&SN$(sRUa%1kw`pS&7ku%1DgXBxPHj#7R`JFQXWVZy=@s|tuyKypaf4BQ_;R{u z4wcBw1@)}+&o#H0_1jf16nBzoo|X5mbsrt;yj}l&-L~CC{X$ofr|EZQ5!d%?wN39> za^EALKKpnH1{CP$wJ)9eNv-F(6w)t5N^_L+XB`mfJt-gelL_P>AN z3*P#g2e2Z;wlN^LKOD~zBCE%+-Ji0yQhNnW(1H$Lp>?|+dQ9{e(Q zE3qB%gGq#({IXThsV#AJ?+K$A$4JI9n(>TiOrsjt$i_Ch@r`hd cqa5c*$2!{aj(E(Y9{0${KKk*GAQ%7uJIyM;H~;_u From ce6b04323b9bf1de7a5de694c99c0a2f5541fdad Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:25:46 -0400 Subject: [PATCH 06/16] Use visual tests --- ...reate_standard_qrcode_graphic.approved.gif | Bin 0 -> 10848 bytes ...rcode_graphic_with_background.approved.gif | Bin 0 -> 20951 bytes ...de_graphic_with_custom_finder.approved.gif | Bin 0 -> 10548 bytes ...ode_graphic_without_quietzone.approved.gif | Bin 0 -> 8136 bytes ..._render_artqrcode_from_helper.approved.gif | Bin 0 -> 4027 bytes QRCoderTests/ArtQRCodeRendererTests.cs | 24 +- QRCoderTests/AsciiQRCodeRendererTests.cs | 268 ++++++++++++++++-- ...can_render_base64_qrcode_jpeg.approved.gif | Bin 0 -> 2446 bytes QRCoderTests/Base64QRCodeRendererTests.cs | 11 +- ....can_render_bitmapbyte_qrcode.approved.gif | Bin 0 -> 5332 bytes ...apbyte_qrcode_color_bytearray.approved.gif | Bin 0 -> 5332 bytes ...tmapbyte_qrcode_drawing_color.approved.gif | Bin 0 -> 5332 bytes QRCoderTests/BitmapByteQRCodeRendererTests.cs | 13 +- QRCoderTests/Helpers/HelperFunctions.cs | 36 +-- .../ShouldMatchApprovedBinaryExtensions.cs | 123 ++++++++ ...n_render_pngbyte_qrcode_color.approved.gif | Bin 0 -> 1395 bytes ...gbyte_qrcode_color_with_alpha.approved.gif | Bin 0 -> 1403 bytes ...code_color_without_quietzones.approved.gif | Bin 0 -> 1130 bytes ..._pngbyte_qrcode_drawing_color.approved.gif | Bin 0 -> 1395 bytes ...er_pngbyte_qrcode_from_helper.approved.gif | Bin 0 -> 3380 bytes ..._pngbyte_qrcode_from_helper_2.approved.gif | Bin 0 -> 1403 bytes QRCoderTests/PngByteQRCodeRendererTests.cs | 91 +----- ...nder_postscript_qrcode_colors.approved.txt | 121 ++++++++ ..._render_postscript_qrcode_eps.approved.txt | 121 ++++++++ ...nder_postscript_qrcode_simple.approved.txt | 121 ++++++++ ...render_postscript_qrcode_size.approved.txt | 121 ++++++++ ...pt_qrcode_size_no_quiet_zones.approved.txt | 105 +++++++ QRCoderTests/PostscriptQRCodeRendererTests.cs | 25 +- ...reate_qrcode_standard_graphic.approved.gif | Bin 0 -> 5332 bytes ...e_qrcode_standard_graphic_hex.approved.gif | Bin 0 -> 5332 bytes ...rd_graphic_without_quietzones.approved.gif | Bin 0 -> 1828 bytes ...h_logo_and_with_custom_border.approved.gif | Bin 0 -> 7915 bytes ...logo_and_with_standard_border.approved.gif | Bin 0 -> 7683 bytes ...o_and_with_transparent_border.approved.gif | Bin 0 -> 7710 bytes ..._non_transparent_logo_graphic.approved.gif | Bin 0 -> 7735 bytes ...with_transparent_logo_graphic.approved.gif | Bin 0 -> 7710 bytes ...can_render_qrcode_from_helper.approved.gif | Bin 0 -> 5332 bytes QRCoderTests/QRCodeRendererTests.cs | 34 +-- QRCoderTests/QRCoderTests.csproj | 4 +- ...erTests.can_render_svg_qrcode.approved.svg | 227 +++++++++++++++ ...render_svg_qrcode_from_helper.approved.svg | 78 +++++ ....can_render_svg_qrcode_simple.approved.svg | 122 ++++++++ ...ender_svg_qrcode_viewbox_mode.approved.svg | 227 +++++++++++++++ ...code_viewbox_mode_viewboxattr.approved.svg | 227 +++++++++++++++ ...g_qrcode_with_png_logo_bitmap.approved.svg | 218 ++++++++++++++ ...ogo_bitmap_without_background.approved.svg | 230 +++++++++++++++ ...ogo_bitmap_without_quietzones.approved.svg | 223 +++++++++++++++ ...rcode_with_png_logo_bytearray.approved.svg | 218 ++++++++++++++ ...qrcode_with_svg_logo_embedded.approved.svg | 210 ++++++++++++++ ...rcode_with_svg_logo_image_tag.approved.svg | 212 ++++++++++++++ ...svg_qrcode_without_quietzones.approved.svg | 227 +++++++++++++++ ...qrcode_without_quietzones_hex.approved.svg | 227 +++++++++++++++ QRCoderTests/SvgQRCodeRendererTests.cs | 52 +--- ..._xaml_qrcode_standard_graphic.approved.gif | Bin 0 -> 5332 bytes QRCoderTests/XamlQRCodeRendererTests.cs | 3 +- 55 files changed, 3664 insertions(+), 255 deletions(-) create mode 100644 QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic.approved.gif create mode 100644 QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic_with_background.approved.gif create mode 100644 QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic_with_custom_finder.approved.gif create mode 100644 QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic_without_quietzone.approved.gif create mode 100644 QRCoderTests/ArtQRCodeRendererTests.can_render_artqrcode_from_helper.approved.gif create mode 100644 QRCoderTests/Base64QRCodeRendererTests.can_render_base64_qrcode_jpeg.approved.gif create mode 100644 QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode.approved.gif create mode 100644 QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_color_bytearray.approved.gif create mode 100644 QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_drawing_color.approved.gif create mode 100644 QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color.approved.gif create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_with_alpha.approved.gif create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_without_quietzones.approved.gif create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_drawing_color.approved.gif create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_from_helper.approved.gif create mode 100644 QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_from_helper_2.approved.gif create mode 100644 QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt create mode 100644 QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt create mode 100644 QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt create mode 100644 QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt create mode 100644 QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_hex.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_without_quietzones.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_custom_border.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_standard_border.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_transparent_border.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_non_transparent_logo_graphic.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_transparent_logo_graphic.approved.gif create mode 100644 QRCoderTests/QRCodeRendererTests.can_render_qrcode_from_helper.approved.gif create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_from_helper.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_simple.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode_viewboxattr.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bytearray.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_embedded.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_image_tag.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones.approved.svg create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones_hex.approved.svg create mode 100644 QRCoderTests/XamlQRCodeRendererTests.can_create_xaml_qrcode_standard_graphic.approved.gif diff --git a/QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic.approved.gif b/QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..114892d22c3688200400fa0d450a9164fc0f1e68 GIT binary patch literal 10848 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 UeOs}n`mlQIw}Sh(tr`dbJI&%*CjbBd literal 0 HcmV?d00001 diff --git a/QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic_with_custom_finder.approved.gif b/QRCoderTests/ArtQRCodeRendererTests.can_create_standard_qrcode_graphic_with_custom_finder.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..04659bd5065945b870906c7ba85b18785fd548f8 GIT binary patch literal 10548 zcmV-4Da+PJNk%w1VVVJ&0r3L>00030|Ns9000000A^sFgM|mJqPexQAM@dE?MQ&qn zWMy&yA^8LW00093EC2ui0Ga`s0RRI3oR6u??GK}zwAzca-n{z{F2Oh^WQm-Cd9tL7 z9xA-1t^MM$G3&K_1LlOgL67+31(ic)@#st%m(ynTNrhge+HUr1^@hRNv3aM8rGIOr zI_+k|qwiyzF1OFO_28YK=ePGqIB2McD3~}@xY);ds3-~P7)UvZvR0zG!U?#k`Pmuj zSxS1^W}1o$r`pOo;~ERwB3n!QVw;N#y6XG-I~#mEyj$E0oGh$N%xvt8ye!>3{ahVQ zoo%g+z3tr%K6)$;O@3|8jsC43F3R~1A1^;oUvGbppRd2q-|znqFrYwqq6+G2caWVz zgS-|dWH=6DIf>^ern9&XBf^IN8Xa2yo7Y9(MU;)@;s{aDbuJvr#g*7BdZUsNVsC%3bt$5uVTxR zJ-fwg+OccRwnZzZj9e~c=$^4VS8ZIpeAR~d3plXg!GsGNK8(2VUcGf0-*udq?PAG| zDL=Lx+3jS_l{sJboEdIs(VweUT6$V)YSTbbw^sd{byL@~Vb_*T73x*pv~yd%88c(> zjV6n$H0~0)Oy$C75M|$l{7F_ViYan~@fpjHB6D8jdy2sAFH4`3M@2Gz!@x zWJL0an{=vKC!BNcnP(3P{)r)=3kupGp${4wA)*snSfQd94jO2rkWN~tq?J;7 zXhZaE%4w&behO-+qGpO|qLz|+X{xBMYHF#AI@;)|t*SaJtFOv>tE-RFnk%pEs0gf` zeBw#$iG2=hY_To>2CL_>&MNz&uuW)bWVJt98zi_2s5lO!8gFl zXvGv?jO(tpYFul_y>1+*h6w<7+;Y!Ncind1jd$L1yUTaq7w8Rm;DQg{q}e$Pj(FmVlYJuqjys;f z3aM>oz|~Y$?m6w2^JM$y zwChg#AGsUw`vAfFzB};6mk4|Ei*Fqjtja$xIMXHA4mtIfdt$xt)N7wR_S-RX0`JNa#Z|NZvj$6tTA2^$rMtcOY;b0Ngs2sJ0aa%hl!?{nS?&Bwv~ zd2oL+1fl(CIKCR15Qq5dp9)_Hz4tM%C_oJ25OWf|;q4)b$5Y}Fm`FM&1_6qqLtYV; zC%dcvs4X&A#3I^qL^|VXE_7q$o#(`O#u$YWez!AY8_}}HHBP3B$x~g8-bTmQ`E8GU zbU+^U$Rxv|;E%4eqZHy;MK~ssjh%a=8W$-?Dj>3qilihX!9qyX8PY4PgF_}ymqj<$ z?s&wDViQTZ#8dVzm8M+f6IEHo)_rn$o%9pRX1TZol~8jhtey^e$wM3Za0a`CTrVw` zLKWaLaV@;%G6|>4Um7!*B$Vd$sENWG&d-_!DdsiT@Jwi;Y=Gem+(0HMISHckfa;uJ zJ0bYa2fm}6=B$|u>50B*=5UxT@Fp8~xXpdeFrYmgC>;FB34H3UnIcnYJ~4z)Z2D6F zpdyqgGPQY7ZXWZWzyzm5?6Rt+4~vOn-3KuFbBcQ-oz1MLHY0>QpE9 z+^T(&*-wlHji41}Y&H3LwDq}_p(GRRLlFA{%r0%C8`Z4(L}A%+%7?D`U?(R`8!FVU zVVw~a6>071Ot2nSv5AH3W-Z!HjS4NHlLZ>lLK_j!a&($K!)<428(GlyR%j;wt%+dm z%2$;}w_zm8>q$rHN~>8>w$1$mPqp&gdyVywXhqYw#@opBimh$o6{HlZYS^sGcaPys zjC&2xUbd3el1Uw%T8;W&rSiADMCz|t^4m7E0Ja3rT`*q#id~sPxQ#V!V<{z^-P=@n zu9NNV913gI7*h1H1r=tAN!-|sj<}-}iKs_q>)XfzH)sB=F*k+l;uh=n#fF6yIl*c` z)(UyZ){7?tOUv4Mx)p;Uj;e@HoXr(a6v|UhF)??nV<~?#4KrSBWtV$18*6#E7aj9) zZM%>hD-+B)^Y9EBELYr3SdVSp-z8)J8V!6>^}12f zl{_?o1?=dj-T=Yobqjn$Jl{;atGX>yZLF&V-F^k8z7*0?nO3e5f`J>!wG$`ypf{0~xjrWplQvyybga`6A55 zHov_+TQh4y+{taSPo0f#gzq~e{a*O80S;|}^Vr(V4K$ox3T!`PT;s9vwXbPyQ(|uz zCl}YQcRdYiMqkn`r)K%87tQKaa|_XHNx7ikTNqAjdgoHoxnWTM{_3;J+|Z6}>d;*- z>a~(O(o6^N(Z6EzP3o11!|t`NbB*geFKgsI;X2oOQ*y5D#Nap$xW5?=aN%Ct;VTex z!V^O9+tz#9-PX2aS*&k>X8G;NzA?lHZtroAcFF24a^b0mZX~Z0-9$z_K3*H23%qN_xPR8o!sc#@J|0t! zOE>FX-!sVZobd?jUMXRxE);;yUGcuW(M6Z!@*kP><}cmpO;`CGgPvftT-wPz?|#n- zZD0M?AnJ)GI;5q)U!|uX{h?NW``K@O=MSCyg^u-~ZJ+!9kZ)Z7edWE!uda1+=6?Xl zQ5=SUQd4YmcWj!MZ?$!LuE$y2wtAfBfUVbX6i5QHM_K-%fftB(6^MbD*K8h0Tod?# znU#8Irh)iDZdR3er)7AW@NOh9gCkIb0dRvk7=x>|gIq>`)ffCqxC3uBND1t{gg(o-yPq-ko_X5SYYr|KD{I`3-mWF0X zXIQ6(dNzP@_=YP$hEsQQ;DvqbcYb?letn2^?iYX4$A<@&hyTJB-iL^ZsECUge%V)l zaCL{M28fbKeU&(K&)0J0Cv|@af04L}kZ3M}W`79(czXosbp-f(ZpelKD28x2hjuuM zCdVN$CU-Q}g`4Mjtrvj|ID}sKS)f;3!8m%t=w%Orgs-Q1S=ftM2!g&?F~B&C(dZvE zLyan+gN;`?iq}9e=y)>NgYI#I)#x%CNHkB@gkuJc%D9Ur$c)LAj^#*;pB0bfNO@d{ zj>*`J?YM>K_<;JlTLu^{}e|;u=Xt-B$xQYcBi)*Nm1WAeoNo6U+)mjO8;;uvp-hmDU3nXToGkx7}AX_@K7joT=W_dt&i7nT*VmxURc zW+|6%d6=g;n1-2}$2gCzDKxO>nW!n6rkR>+H=BS-nteH%eYuWynOp<8b-q_!dq$N? ziIoo7dsTUy#o3fsnU%`PimCXJ#upnV>4-NebUDeBKDnLIH;CP-oj}Q*;_007FTBl0qqe;VGYjc%IDvcZnZapL%$o9mc$%A;oLqhQLXEb5GF>ZNV^5WvZxR_CC&w|^DNp$(d#h>D=ixtzo)sk|4V z2brK+W1R$=kpTL9I60H(DU;v->5>0goAL*$BDxjXIeedb2uL`Sv6Q7-$ouAr~ zJc6CqiHW<4s%dkoq$-l@Ns=!)oo8raA~&g*ntzIltPDA!g({1Q8mWi6l?dplV&|cA z`lfOUqd6+5R+yu9>W*jnqushF-};sHn5SYHqU$=M+WMvODy~bDrhXcx{^+hn8Xrd* zrIlHw@+1(~sF^(2jqow0RBEK(*dFFsrtFHQ@CuvXc8h@uucpVXUCOY3T9*-muI$*S zZ5pvUN`)qBi+gIXY6?*m%MpZ{q0PFWEo-PRYmi#$tjF4wkBXreYO~RbvpXwxFfyyX z3PzkNw3<4sx~iYN8mt!o`Kz{iwCWj>oj8)QO0`vMwN5gWwW_LH>xZ{GpHd5+P8*a& zyR=!0v}H@I^-`1?DznQPoEz%4NeQXX*{I11p$Gc2i+Zzp`(Ycau7x?T+X}eg8mH|# zxO&QW=}NCEtGM!*vN&3}8jHAO`l1+nj3MiqW7@DWnjY2In3FlM_3*g_8@kqbuuTfO zNxGT+TCl4NkB^(U9xJkSO1N}duCe>BkXwr;)3KcErtwO$pc!r9Dx?=nxwo6Ibh#kC z`?`0qwv0Nr66&)H*|yRuw>c}E)@z~F%Cmf{z0Zq)nQDlps8rJ&+MoLppy~O(;M;LD`?6mL zz0cZQmKwcmd$-zqt<-zL-3u+5E4zStva@@yuzS3HYO$N^t&ID@e!IKPyTX%exRU$A zC5xwr>#!pX!!j(q=GwyRz?ce4gQtt7_Q0ySPaE1T*ZiKyf(bUa4Mr#46+bAyyvRIzRR)}EWtW^oNhe564|p98pjbl z$94>@KbygH{IlYlpG(`c=!>7MdcIvNz@qxU;fcWV3$^r%zmY7-ll%bsJHUkus+sJy z^ZT`;>b?s9Ovs6B$m&bJ0IV$x43T-f$FA6r5WKgn{Kj@G!82R4uxOOnOQ~@-kHJg4 zTAalVa=B@|xL}OJ<7%=V%)3?`#Z$bzW-QEKT*EqCj`iweqH@HjOS(^brAL;qt!uiJ z8D!pk0!|E${z`9(X}c6l%#~Z4TDs22Tg+wr&cB?@$Lz%!%cja)yzwlt^Blt2>d(7O zuJkOBs0_=h46}87z0CT`#;V8Lo6FYfz#Htn3JuZ98orhs%43_fr3$~DjL4sC$Q+Hf zoNS+^T+&cW$t%s$lN_`ueaWNhw4}Vr``giiOtxa{zaUM(Cmpt-tkJftw+$T87EQqz zD#s81U8%BMp-0WoM$Obht;_CA#x$C|>3qexTh&?J%d%UqAq&<6&DF|_&Vnn=k-NoO zUBY7>n`;fiU+v8POwIRf%|ILv zt9%ts{m^hsx2>GLuKmE=ThX@x$Ol}&VztvgEy|jF$izL!>&w5sE!@XlplQ?6&kfyG z`_eO=w!PickNn%1{L{$o(f(<^3C!J$oVG>j${g&+8a&IY?a&K7t+`FDaQnS@+};%b z-N97d&W-zwYs|5d9mbc9u~Gci`c2kjeBWiA-8J=#b>(>+x&gWduxa-0pe$~*t)^N?>0bQb}P1htW#v?w>0xr+d4A1`E-@Hl1 zrrp^U!?NYQ+tjIHx((#3&EB<*%kEvh7M;{aj?kDo-9TO4jcm#zo!ls`)7>r7*R5(; z?#KZg-Cqvo>>1Qn4$9$ezKcxV!j0zrd(+?Dh)PivXVE%xvJOz1VEs&mykIJMQ2&uFf?7&e)!v z&xqdQG{eOHQ0nj|;o02bOKi>@KE!`5&hl{006XC@{?&veDj=t{c zmY&&@ZrN#lrv;wtn%?LS-s{iK#UM=SzhcnqozQv?-`eHle7@utZQHNi=Y!tbKOQmG zt=+@DsogE*;;rV{?cIvZ)9H@W>K^6J9p?9r?}#|&H67D5&9rYG(p7%${_gM0&F^du z(@>s$)qcnE?eN-u=i>hDbqnrG{oY9~=&-HSb^g1&TQd9Wg4ie%P-5^G00ZeNF4C{_5ZVeBm}&*c!g{ zi%0Z9{~jH$&o#f_2!8CH{_<2`=_sD_D!=p7F7sIL@?W3hyKcfIui~Xm+Bd(|%-b{^ zFTET8@Yp`?=>60gANNI`_uhWw6JNpdU96+*?gSt2|32l$E%3!X@P%*i=>F~o+}u`> z?~_mY^7Z71yzXhKKl&Krfj@-1JEw*dLz6Lj12#FEfcPuP{IJDg67zAL_pUkLevfQ%?Z#=hO!LUs?!OIYX;)^A4^lT(qv-PU{0Prp?w-k&%~n+dHF&9_x8N zQO_hK5mCmO62W93El>#XCyiJg)L|;?+y%KHFR%zMpOfHfn4CsO)!Xu#zO5g@BOr6H z-{YhewVl(;t3$!f-Ph+!?+ot|^A*?T?Edk*5w-7&Kt=}t9r+9BCJn(y{NywQh%X`> zhhwm8x)=vf6&oLOfczm+L=2K7Y{W1bGKb0!VY0Y1k@Drrkt}ciuqoo?jGZWPmVDW> zMo^$Rc^aKT6bDigKbI<{P;;nM1~O4#RZ&$Z)tOc^FonT2sZ*b1kCqks)ojtWO<%;7 zJC|-<3@ZTGwTl<8UIlys2NoPx@CLt!7xYC;c*SDI9~?)18y2h%%VJ@&s7%uYRJUuh zdJWwf?OD=n)uvT#8U<#~tv9zU{dqO*sFN?p#{JrYYuTq^2Pa5*|>e8zKR38LtF?)A;s~c5N=BU|F9rKi{Za)EbqPJcShXL--pkh zKgLz>$gu`MPAZ9Kz;NPeN5E~w`Nk1%#6hRQ2ib7Jk0{L;(WE0Q&J@>wgttGEewLk zlx=pf7o2r~S!Ipgt1-loy(`+qqRp#X$t*4P*Ig_1Ox0LjO&3*JagB*wU^Nq!QgexI zH(prP4OZ7)@1+z}afOw1P<98-w_kh9ZFF5np+h%Ud>4M$-^%Eoz-D|0hV_O zJ;jTYOpWYQb51;(d@I)PFwA@*>0N|E5)vw z>K(0alJ2=Ne6nu5anyUtuz~Da?x-REyxK;?vnCsEAP0|oaZ^C{+2xkGT$$vb!L%Ic z%w^`ebCo|Y(`GSA&$HyqhZdUV&CTdJ=!^cH_g#fK)|lZ-HwNKi+I8nW;YjN(IAVAC zwV2%4Zxz0FgpnUM-*m9yyrWNjVpr1 z3*W(v7*jBgCwjr0gdNlFcHN)sC*DS5_ALUN6qOk^Z+HaaUV(RG~5BJ09rx;8m6 zid1AJ6h((bEVeS1lcQx2r&!BZ)@g{ZvzZ7*IKdXCu!1ifVX1^k!r{sAn6_J{3CDLq zXqFI|IjrF|lZi|S8WV-slV$^rDa~(gGn(aFVEVw9LjxL6e*6RA|N5stZfy&H&Z^%7 z?g^ND@@1d<9N;=LXiW?XbhXlK!=_aq6zKTMbC#(iLOta z=3MAAvne|kDztkBB_=^h8qxHPlcUz$p%!84OC&NeXf!1y5K&pmA3hP6M7(89b*f5R z!m_7VJe^Tj$;F_3k*Q$QgoaHR; zd^gHX^C~yK=`C)5+nZ7VJMXZcb*yHO>O;d$IKoMk>`p0*Vaq<)ur{^uVj8 zZxw7_9VkM57hC-{G`Q&PW^b+e=Mvi2&iLK0fwkGvlnNTY1U7D;TkBr-HoCl! zM(%qrJxxj*TDYAF>(yC{8RUH>Zi`1KvW^{dzS5Q2U zbiIVWt!@Xc+DI#y)64y@r0*PTUei~;x>j~+nLX*`R$9Q8#xyf6y5|oznA>Hp@S8mw z;wpaI%|R`9xZ8YTaPzdYY!>C{rrct|&Dh0Q&2hY=ELSB8O8+9a$p<&aEEi7!XlnC$t!GTb)TEv zl@;^-p(+SG3%fK6k+fI@f-u`qYJ+^rI)b*1+Dn&*82^oM(3OpjElMXUg(1oBP6X z2D49X-fcL)o92ea_sCB#a@7x<;{oTlz3IJgjqf$}SLX8VYyTnv?4I|%_xG#S>3^wn^zWbl!>2pmZw=cNkLwVu(e$nWGcD>)3-FE& z1N!d)Ay5GRPuc)*z!dO<5KsV@jktPk03lCTxjOhG}0gdnjTObFOkob1+?s{+o7ckoR zj?b#j0G)8~a0BH;Yzes#_|)&F>W|Du5C3E-{vb=j%<#*=FwHoO^hhk^D4`4GkoUf? z$||nNx{u@TZ4YBF{A7>%!VeCa%0=dI4wdf^5%J&z@dyf04zrIBy$|=Q5Bq5UPZ0a9 z^|0y^Bd*}2f&7}U5xMZ@(ooG#Zr#|h-A=I-b?Wli5A(*240EmlCjb;fk=hdP@1jlb zrZDcLknpU{2C>k_c=fUp@GkMR&M83QjK9grMH&>T<7=sFMzF;E?84IlgQ z+5GVbAx{fMh92vYAk|RAQCP@;;Ns$33*T%_f8&# zF)R0PE8)@c>MkFljRSL0EUz#K7Y_-+(E!kL|GbgZ+@~95FrND9FHO({0}}>at<%JD z9`n-t)N<3AAgQ6 zk1!Tru^>HD`a~}&X>KVeatxXBA)B%xU#>S%G4yQHDluUZGcq=HuqH$DCX+KKe{%b1 z&m_T5%7PN)VzP}?(m0*(CAD)q{fIc506U+r4q30sg!20Ga3r1o(>zg66Y-EbJx&h` zizs6XJhPJ_(J&%Y#}pk>DK&3D@$>$eGC12%BKt%Z7l1yCuLU2>py7rH(>1lL||-D?OA%RWmV1B^os}_)72u!HX}QD;rBqMi0X; zYm}buaxPHxKvy*Q6toIM^FuE)L{D=>_YoHlO&875E=Q$DeUwA35J^Q8L@jhmhxAC> zQ8b5cNe>Mni_%Gd?+dFE^cGS#DRMvq6gOpV75TIC^CPW~Jtvh<#SohP+O>;Ht@{>T5ObB@5eB`ITbxcmQY#5Tov_X zQ8q;wwFgX=sRC8rGO;;fwkH8mI;(R%J=RZe79wH)^HY(s{!Z05*HBe&Gb&q=Dn0Q& zbMs9x0#<#NUDZ`C{gnc#R!I#sSIhEhiFAg{6>5$3E9dett2S&~Gg)sDSGP7=4RtTA zOYH_T2D$ZZP_ z_qEiPPT4kFu@!Fn_b|mv>;xD`>9&B~r+vj&e$kZ~C0AGVRY-%he%1GW8@Pt{R)O8s zfh!kljgeOE7jczNVEMyjGgnMa^>k18KO1&oxp#X-xP?PDYl639k(Uy+Z)7Phhcgvq zQCE3=cvE{fh=q8Fk)e5e7>Fqrhhvy|e^^kRSc#ALiObW7llFzJxQekDg|%3FVfc!- zcvY*HeLq-?Cm4jamV!4}g3s8DD|n3ExQxkojpMkD;kfAx_>S>7kM($u`52()IE~TR zSL=9?<@k*YnULxDkQF(R85xk-*pdJLSdit|i@BJR!PtAdIEyP;lP}qYIeB#Bw27fO ziKkeUOBsocn3S0ql}&k-O@xSHIhJL)7E-yETX~6bIf_TwmRY%%rP!6B_>;rfl0i9> zzqpu*d6f4Y`pe8Jg8tnge;7A^DlF8IiMjk++$eA32)4Ih?_HzxueG&H0?s zIh}JflC621+xeZVIi9~6p66Mf+1Z}4*`4uOoAtS#{rR5%dG4ColaE=M3!0ginV>iM zpcT54jTsPl`ImE9l_xr)clnle`IIF(qfu{`J^G`E*rGN1qJ5d9H=3e3x}q@}rBOPe zAKIY@`lXTCrDM9LX&QF;nVtdddZ+VQr+a#zxw)TtI;Vl!n}<56eY%X*d8wJYsh#?% z<>IJ~`lkb$sH+;Oi&~tM8mq5*s;ye8wK}VX8Z&H~p=H{j7dowB`lijgtkwD=MH-k# zdamWVu2s6E>H4luI<7&wul+h@`8uTqo23JruvfaU5u0||`mND=t=oFB9hmT2i!WcfqSMaJGdKLxFx%xjhncc*|722t_M4@n_I6_{h>W=28WpV%66(~4J7$;^HgLtDj$Iz(8*zjm48R;lli8#5FX=Z3c7%Dny z@|nYks)^}nskREKdMOL*`D&MHMyk83tLXdd%Mv^|ELfZZjL5sp++|vYdl>7=I!!ry zYt4zh(|S!So!dN4-V*(Eo@3js9kCway-f`c$PKTlSgybC_Lol1BLa2{fqCp+qw{*ohRWu%t|ZIj9M>WK5PRKgpbmRjNv=Ft=v4$~A1wqfN{5 zRADwPsGb2Wx~)^BE!;N|~y9-S^b?Yx=KQkq4Tj^%bxM>s9j21XD z;V6zN$bH*0a^0{`;Zr8O?w z6%~hGedwV}Vu2_Jh)aZc;)x}S_!SwoWvE|q0B&L2iACLrV~#rRh{1~*22!I5L25B% zgGXq^B9bZ|8RCjgGPxv8CN}Y;lua_};%?#?Gh+!wV)5lg07YovKxNW2pnz%~IH8#Z z4w0rDaUS7XgW+4h^j_mbe=9EDx{BIs^yPeZYgK0Z} zuA~!#*)NhSMhOOqSbl6}l^rLEViO#DbY;jOU$LvM_>K!fw=!PLW6d_-jPnQf4))u{ zJ7b!0$|H-6a*8G&ebSUkle{s~EfIZ{$9VkQGt3XjOl-z;K3uTCZ!Y|`LJW$epx0oZ zv^5xGJ4E%%+v;nzr97W2uX?89S0CQ+a%!&L@;a8E)mX#*wwD#pZ1uc~|Lxx1jRT%; zop--$ciz_jw#}!{E#I5D;2GY8vF70=CvM>e3~spS7RN2D>5;R`xZaRE?l*n$s@u10 z;|1CTsH-x&XkztIZIJOMKz0D7rR$A*Cp{wwR$5L>G&BqE-;J@ zbeXrf*Sashu6kvp+Zhjut%yAli}16a9NoA_Kt@uKdTbUHrMEk31&&>Xv?LWhnZ_u# zQG;NV}Cgc1|zwJvnMEgrZZ2d%mkkED<)*8 zG|_oZaDFb8*z_LwPy$PW$+CxSiX{3Sqq322DB{ONf=t1=V zpqoSa>&~Wedav+E0D{lYM?UD?VE}Otk?NuH$2?KcdQ?sanW;<%A~*kJ-!;BC{(b zYp2qR3Bte*?FV=*59kWv&B#htvX8ASFIYHOzXEodf>kUr)!A9jZWgqoC9DZ8i`r4( zvY>LcD{Il}+O)#fm20JKYi&E*xMnrAyv4#N-=)%$?)JCB?J853%2hP_vbW6tEpBj+ zTE*yk^0`}j5rA>CkBrdUuPdox>_BDU&jHZT9*uoEI_`(}5t&DHXVh$5l#XN4Yhwh0> zAb-!TZRKG_URz)U7kQsSCUP#7JY*pnG<-u;D-G(FMJzkiy2EAT8vA?S;c8i=VBPYV zExJ{Dp_zIwWpkPtx!*8r@u^$3FLK44Sob8`)fvXpPz>nK;5%2UR5iF*9kWcpaehAr$nb8KVGzPQ54-m!%{ zZ0%~}SYtj$vA4es?r?XL+0W*-j$vGGXD1f1G)DKj1(@y_yYt-yY<9De-7z5#RNuF@ zwJz&wz6RTpp#3KJzv+tb2Jd^~OOzphtlJTo6jd)Wpe(Bk1oZ?a3c^QDeOnbcIb58lnJ8tP$Lwe+8(JLS2OWqIz4ZP)%dCMjLZo8Me9Oe8yxzSlJ zbf*qIy?YKhs7+7WvH2!0&?_o5=%H>lmiFQ^Px#PZUh{p&_U6+qVS&@S;I)2o+!YMvfy;jNVuyR%Z~ykR z|2nt0=QSTn?$wWzndOometVl9e5_Yp>CBh&#FKncf?+6??Z|}Zn2o2Gct}W&>KJ%sDR1ydjZjFI&M2210*-fymwCA(;z*b2*p_`c zkAca9Y3XA_=#~@)m(4hrV;7bkNOoEod-oWSxEPEDXO)hrh8Osik|~e@QGXqoikpU$ zPHB^OD4HPGiU;X*s0oX!iI79dkVRRO6=|BF$&|Ahi=p|HL77K{=2N7zezpOLF==QB z)rd4zbBUG|Gns#)=#im#inKX^P)UF}$(y4ol$AD!p?Qcv$%?J{iAG7C<4KbsiJPuT z2A4^UBuJH+`H!8+nUDFNRhfnPIGK(A$(RM1nDY3R>L!C~8JK*DmN+Pwh^ctv7@*cz zmq?nD1zo7P_DXns`f~kM!w*7C4`3IHLH;o&;Hj z>xq?Msh=iVdm4#fuZfg|_>(m%fzN54skxoE*`wZ>l(mVS(|MiR*_=ZvkwprXyXl-e zC5bA@bMsd}dF7kshiExfoKPxLg+QZt#+ugoojw_+7FnL)DWq11qqJ$8H(92Fn4~<4 zrhOQ5-ASgod1_6Xqh`u-FB+dJnw2LSqkmeD#TaZbI+edDmWb-219hN)Iicpbp%?0* ze%YZ3YL1Z_j~@!4{RyF;3aX+1I;m%gp`>c50$QpS>Yo~ldK7x8{n?+;B#enVmMa>l zT}i04Dx=0&d$$OvmD#JBS$EY5rg-Y6;u(}jiL6iBeLs4vJIbBpIi|%5opLIs(P^XE z3a!n`tV+p~+lq%HNu2C=XznLe<5s;O~#s;)Y!mHMiPN1(B4vIYvPt*U~dDzh`Imn<8zn7XO^X{tC&vMC#)D$A;b znW=>dv{XT{_c^HWDUh@Onx8EyhK72TAIp_c#* zuzriLbb7dEyQF{nrj47p{TiG9I=SB3wHv87B8ZtuyO~N$nS$!Hwd$)GtGQ4+we~5o zOUsW-V6!>Pvm83IuuHUu5X`_MJcqX$v7bA=-fOzqJF%xLmfUNx zsVkqjDv*yVkHvekIJ~nvyt^_un8!P`k}AC2_Pa-n#NHOW{8_{|yTi>3#4anvwEM$B z8^y`nvm$!2Ps_!kTeT?6v0LfF`xvYpOU5azqF*Y)AN;iooWXCZtOV1!gONBA6?mDGXx~}M24vc)99*n;eT*qyEq$Nzr zmaC?lyssPo9LSfv#+8h)XY0p&T)&pg$(}5>_8YhTpuHHYt1R5b>e%7a`Vz|7yp$om`+`@{A&gwkIHQc?@%gdPcIykH42KIXzoER! zq8!HzJH8_Q$pYQL)T+<-JJKCY$|mg6d5qFA-OmpDnOQWtv+TLQs>>@(!|o}@>|DJs zoX$P}-Og%gyw$A3+>D^Xtg2FtyE}``%xuk0%_P#y)m=S7_*Rx+jnz*L&ev?rT79!r z&AiJd&UZ)4O3k84ZOf&5)Mrf3D7wX747y7VYtre)Hx1J({lFw`%3Mp+gnQ3_9lwQb z&@C<4=li}Po!FUt%KB^5%dyBy)W4Ce*;Eyr1+2FcZMTdK$nLAjCjGXcEYp&$$2D!) zr5)H9{L%?|*sVO+t4-SfeAsHd)1LX(UwqC@o7C8A)Nn1ju^iX6jMTXFx;Z?%X6?j9 zdyRlu%+Vdr)-A-uY@lAv-QArdRNcDY+|5`#)@Lod*p1f8OT^c`gNTXTbPdK)>&rm@ zUDU=+z4o2g!g{^)-Pe)Z$AGQSr(M!GP0+QizAPQW39Z_XUAZ}3$_Crp0jsaHja7zx zz@d%E;~>cyUD3!9z!wdFd<(CLO~`VrlacMm3gzMY)X0?y#9eBjd>+bb^P z6fS3eYS+13#y{T6Lha6X&AEU&*TJ3K@ZAz&;_tJZDZ-s~;j)$H9{ z&gDNr&59|^Wu48^eY@H`-q5Yyt}E6|oHXPt%WrPA^-bJLzTEKb=1G3vcMi6ttgn0C z;EYSh3473vE8&5j;-1{+H4f0Xedr85(`=mEAU&KQzR~S>;he30zuCw`Ww%fN;pd2M z=%4QBg6`P1&F6p{;Ux~(3VyhWzPK+g$RP~j8f?GDt=~R9%qMa-3 zMy_^E4d%*B=1nfe z{l#}~=ls3B>Mqy$&F*y_?DX9OI?mcJZR!bb=q(P%3y#k)PT={D<0jtfF7C?qtm=*J z?~NXbBA${?0piWD*<_XBkhtNOj?o0{OM(5$1^?;@-q0w{>Wtp+H16>+Uace_wyJw8j^sL9vgl3jXCBqhUG!4_KIYp@-qW4C zP5%|v4)sx=s!T4jY@OCd|K#Ex=Go4vR-WaMdL-|C>%M;6GQZ1V4D7`I(?w3YKaboY zU(l~@+O0j~E06G?PUt89=yng=aF6eKKe+df@}~~?i>>dGYvB{GoOH|KG}76dez%=I z(Og90EMNF?ukinU_j_OOeC+oh@86%#_j5nE^sf1Y&D*Hn@^SCl-@Eg)3fymP?q~1g zY2Ws;U*~S0*HnMil-k@My3FG){9S+bL;USYFN0FA{MBye+&=t3PxZk+^-%8nyUhO*k&!BZYhs;sJ?oL zpJUi4GJ=PsVF_p+7f+dtR<+TlxU6o=!{WX=E3oS;3&Y;)nyhtm!M3xK$|jD9bEe+O zhbo?Ek^~5W3W*4H5_uMV8ifvx9+e`IDx)p|Cz&>#Itd3e13(o+O%0(yQBg}<8BJXs zQ$IyPdu)qvB}pN9DSbJCJyk7*b&D^LF^z3yCvlx~n|q(9f0J!@v5A+fnZLcmZ?lod zxixXq)z{hE-QVHk<>%?^?eFpP_4oPfwWZIEBG(PzHiHHK%^my~jUly!52Gz~7_l5h zf)cMG{D(1^xQ*qstwJ=5q{v++i+2NpcoL}v#AdMvA#|vp2`Q}5!V59X(4Gi6T7kyp)P$DM@sI>~Tp0 zo2+iVD0Zw8gwlG;QaPna%Zx76io5E|rFK8lwwaR|KxHvIpMO)%|ju03{emiRpBxX41;tsNm*z#(aLs=Y|%xr9Mn$WKs9d6 zPesj*xiAYIHC0qY>k}_QLnT#AQ`y|q%}!(gmCI9I)r6H$&yG_SSYe&$Y&2H`)pOTY zd&RQ@TW=*P%4wer^2cha^X?aIg(Ocq@w}CV+iSVo_DDdXwemx7NDNUy7gux_UKv%C z@x~f)#8=;S0etaYdH1U~M*|)$cmWIe%7bo7oy!fTX9J04xn;_D z^pe$L>s<9zIA!Iv)TjAumb0dZrFBhob7gjF;vSgv=);l@c4=I{-rCowy`Gh7uG6k9 z&p^zET57hL&J*IX2ix0VziSPF>?$??6+2?UT_${R$oO8o&)cki)mgjSzT0WAtB!ke z%;9#MbGgstn4pHg&DnzP$C-Ew4Q5(IsQtbirQEndl+A zMH1(qXAYWNaE%O?dGmy~R_5uQR9^aQmp1x$z$R9i-x)tnIplrCx%YeX6$)Quiv2y= zeUM9D+0qE-&&c8X@y}oX{Y&p%$NGy$FZFdyfbnCO_sWN|^`Wm_Hc}w@8o0oVnT!+t zvl0M3!Lyx3j}|2%-tuntJfMj%dEc>K2dQ;DmM# z)4#&`u5T|)f#JfIE;BxhjBH#%7cKKeBi8VUM7*8sAh$y(o=tXAyds@cXDkA|kXF>(9((9VJz~*@yUQXHVb~!IV$XTzLLT*`)Vvjjkdy%&p(jTvG@GgN zgy>PBl3sbrA8OBynk-}hC73Y+<`RA+ijS zS!qHRax#@5%qJ?LhtFaE!0dz9yXPedcFVQOD4RI$L5Lktxu6(3pBDozxoQpIE4y6VuH3U#3#tywGW zsliqb4V3nbl3Mq9P*~~{pguz=G+$a(v)-qjeAQ(*gPF|ru`{sg9Nz&C`_00d(-Ksr z-(IsBS;4pzM>O9OZ;9naSNtayi`@+Ku&Ap#?o%0ACokezs~9M+#9%O4P!W zOp_iX%u-F)c&Nkev!kiZ-w}+_R-{&=qZ}RI8+#ztk6tvUD;-3yUiHwm&T4`yT(nR7 zxonU&DR`U8)J{M3(z1@UqBSgFUFWyee|7|1O9^8&yE)n@j&Ta#Oy_#Wn4oQrS*W*X zWiexU%%@G@VzW%T6I?Q9!kcj))EY(hP}>t-9>=MRcGhG(Ah zoqLz+p-c42P2TdPH@(-dHZaPEt@Wx$H_#b|Xx3fMb;W4h&TVHi#qq3XI79s6cAmJ3 iIS%*fseSDTH>_s|E^v4c*~ft&ZNlFzc*SF*002AU7^Ncs literal 0 HcmV?d00001 diff --git a/QRCoderTests/ArtQRCodeRendererTests.can_render_artqrcode_from_helper.approved.gif b/QRCoderTests/ArtQRCodeRendererTests.can_render_artqrcode_from_helper.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..9f8461e96d27e6eb6433e7851fe6de047e3f3b6f GIT binary patch literal 4027 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/ArtQRCodeRendererTests.cs b/QRCoderTests/ArtQRCodeRendererTests.cs index 439403df..0792c2de 100644 --- a/QRCoderTests/ArtQRCodeRendererTests.cs +++ b/QRCoderTests/ArtQRCodeRendererTests.cs @@ -16,9 +16,7 @@ public void can_create_standard_qrcode_graphic() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new ArtQRCode(data).GetGraphic(10); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("df510ce9feddc0dd8c23c54e700abbf0"); + bmp.ShouldMatchApproved(); } [Fact] @@ -32,9 +30,7 @@ public void can_create_standard_qrcode_graphic_with_custom_finder() g.FillRectangle(Brushes.Red, 0, 0, 70, 70); } var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, finderPatternImage: finder); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("e28a3779b9b975b85984e36f596c9a35"); + bmp.ShouldMatchApproved(); } [Fact] @@ -43,9 +39,7 @@ public void can_create_standard_qrcode_graphic_without_quietzone() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, drawQuietZones: false); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("54408da26852d6c67ab7cad2656da7fa"); + bmp.ShouldMatchApproved(); } [Fact] @@ -55,10 +49,7 @@ public void can_create_standard_qrcode_graphic_with_background() var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new ArtQRCode(data).GetGraphic(HelperFunctions.GetIconBitmap()); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - - var result = HelperFunctions.BitmapToHash(bmp); - - result.ShouldBe("7f039ccde219ae78e4f768466376a17f"); + bmp.ShouldMatchApproved(); } [Fact] @@ -68,9 +59,7 @@ public void should_throw_pixelfactor_oor_exception() var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var aCode = new ArtQRCode(data); - var exception = Record.Exception(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); + var exception = Should.Throw(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); exception.Message.ShouldBe("The parameter pixelSize must be between 0 and 1. (0-100%)"); } @@ -87,8 +76,7 @@ public void can_render_artqrcode_from_helper() { //Create QR code var bmp = ArtQRCodeHelper.GetQRCode("A", 10, Color.Black, Color.White, Color.Transparent, QRCodeGenerator.ECCLevel.L); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("a1975852df9b537344468bd44d54abe0"); + bmp.ShouldMatchApproved(); } } #endif diff --git a/QRCoderTests/AsciiQRCodeRendererTests.cs b/QRCoderTests/AsciiQRCodeRendererTests.cs index f0a4f0b2..d4b55dbd 100644 --- a/QRCoderTests/AsciiQRCodeRendererTests.cs +++ b/QRCoderTests/AsciiQRCodeRendererTests.cs @@ -2,102 +2,267 @@ using Shouldly; using Xunit; - namespace QRCoderTests; - public class AsciiQRCodeRendererTests { - [Fact] public void can_render_ascii_qrcode() { - var targetCode = " \n \n \n \n ██████████████ ████ ██ ██████████████ \n ██ ██ ████ ██ ██ ██ \n ██ ██████ ██ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██████████ ██ ██████ ██ \n ██ ██ ██ ██ \n ██████████████ ██ ██ ██ ██████████████ \n ██████████ \n ████ ██ ████ ██████ ██ ██████████ \n ██ ██ ██ ██ ██ ████ \n ████ ██████ ██████ ██████ ██ \n ████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n ██████████████ ██ ████ ██████ ██ \n ██ ██ ██ ████ ██████ \n ██ ██████ ██ ██████ ████████ ██ ██ \n ██ ██████ ██ ██ ██ ████ \n ██ ██████ ██ ██████ ██ ██ ██ \n ██ ██ ██ ██ ██ ██████ \n ██████████████ ██ ██ ██ ██ ██ ██ \n \n \n \n "; + var targetCode = """ + + + + + ██████████████ ████ ██ ██████████████ + ██ ██ ████ ██ ██ ██ + ██ ██████ ██ ██ ██ ██ ██ ██████ ██ + ██ ██████ ██ ██ ██ ██ ██████ ██ + ██ ██████ ██ ██████████ ██ ██████ ██ + ██ ██ ██ ██ + ██████████████ ██ ██ ██ ██████████████ + ██████████ + ████ ██ ████ ██████ ██ ██████████ + ██ ██ ██ ██ ██ ████ + ████ ██████ ██████ ██████ ██ + ████ ██ ██████ ██ ██ ██ + ████ ████ ██ ██ ██ ██ ████ + ██ ██ ██ ██ ██ + ██████████████ ██ ████ ██████ ██ + ██ ██ ██ ████ ██████ + ██ ██████ ██ ██████ ████████ ██ ██ + ██ ██████ ██ ██ ██ ████ + ██ ██████ ██ ██████ ██ ██ ██ + ██ ██ ██ ██ ██ ██████ + ██████████████ ██ ██ ██ ██ ██ ██ + + + + + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphic(1); - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_small_ascii_qrcode() { - var targetCode = "█████████████████████████████\n█████████████████████████████\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\n████ █ █ █▄█ █▄█ █ █ ████\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\n█████████████████████████████\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + var targetCode = """ + █████████████████████████████ + █████████████████████████████ + ████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████ + ████ █ █ █▄█ █▄█ █ █ ████ + ████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████ + ████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████ + ████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████ + ████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████ + █████▄▄▄▄█▄▄▄████▀▀ █▄█▄████ + ████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████ + ████ █ █ █ ▀ █▄▀█ ██▄█▄████ + ████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████ + ████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████ + █████████████████████████████ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphicSmall(); - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_small_ascii_qrcode_without_quietzones() { - var targetCode = " ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ \n █ █ █▄█ █▄█ █ █ \n █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ \n▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄\n ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀\n▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█\n█▄▄▄▄█▄▄▄████▀▀ █▄█▄\n ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄\n █ █ █ ▀ █▄▀█ ██▄█▄\n █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄\n▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄"; + var targetCode = """ + ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ + █ █ █▄█ █▄█ █ █ + █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ + ▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄ + ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀ + ▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█ + █▄▄▄▄█▄▄▄████▀▀ █▄█▄ + ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄ + █ █ █ ▀ █▄▀█ ██▄█▄ + █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄ + ▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄ + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphicSmall(drawQuietZones: false); - - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_small_ascii_qrcode_inverted() { - var targetCode = " \n \n █▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█ \n █ ███ █ ▀ █ ▀ █ ███ █ \n █ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █ \n ▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀ \n ██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄ \n ▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ \n ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀ \n █▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀ \n █ ███ █ █▄█ ▀▄ █ ▀ ▀ \n █ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀ \n ▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀ \n \n "; + var targetCode = """ + + + █▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█ + █ ███ █ ▀ █ ▀ █ ███ █ + █ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █ + ▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀ + ██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄ + ▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ + ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀ + █▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀ + █ ███ █ █▄█ ▀▄ █ ▀ ▀ + █ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀ + ▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀ + + + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphicSmall(invert: true); - - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_small_ascii_qrcode_with_custom_eol() { - var targetCode = "█████████████████████████████\r\n█████████████████████████████\r\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\r\n████ █ █ █▄█ █▄█ █ █ ████\r\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\r\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\r\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\r\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\r\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\r\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\r\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\r\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\r\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\r\n█████████████████████████████\r\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + var targetCode = """ + █████████████████████████████ + █████████████████████████████ + ████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████ + ████ █ █ █▄█ █▄█ █ █ ████ + ████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████ + ████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████ + ████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████ + ████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████ + █████▄▄▄▄█▄▄▄████▀▀ █▄█▄████ + ████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████ + ████ █ █ █ ▀ █▄▀█ ██▄█▄████ + ████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████ + ████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████ + █████████████████████████████ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphicSmall(endOfLine: "\r\n"); - - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_ascii_qrcode_without_quietzones() { - var targetCode = "██████████████ ████ ██ ██████████████\n██ ██ ████ ██ ██ ██\n██ ██████ ██ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██████████ ██ ██████ ██\n██ ██ ██ ██\n██████████████ ██ ██ ██ ██████████████\n ██████████ \n ████ ██ ████ ██████ ██ ██████████\n██ ██ ██ ██ ██ ████\n ████ ██████ ██████ ██████ ██\n████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n██████████████ ██ ████ ██████ ██ \n██ ██ ██ ████ ██████ \n██ ██████ ██ ██████ ████████ ██ ██\n██ ██████ ██ ██ ██ ████ \n██ ██████ ██ ██████ ██ ██ ██\n██ ██ ██ ██ ██ ██████\n██████████████ ██ ██ ██ ██ ██ ██"; + var targetCode = """ + ██████████████ ████ ██ ██████████████ + ██ ██ ████ ██ ██ ██ + ██ ██████ ██ ██ ██ ██ ██ ██████ ██ + ██ ██████ ██ ██ ██ ██ ██████ ██ + ██ ██████ ██ ██████████ ██ ██████ ██ + ██ ██ ██ ██ + ██████████████ ██ ██ ██ ██████████████ + ██████████ + ████ ██ ████ ██████ ██ ██████████ + ██ ██ ██ ██ ██ ████ + ████ ██████ ██████ ██████ ██ + ████ ██ ██████ ██ ██ ██ + ████ ████ ██ ██ ██ ██ ████ + ██ ██ ██ ██ ██ + ██████████████ ██ ████ ██████ ██ + ██ ██ ██ ████ ██████ + ██ ██████ ██ ██████ ████████ ██ ██ + ██ ██████ ██ ██ ██ ████ + ██ ██████ ██ ██████ ██ ██ ██ + ██ ██ ██ ██ ██ ██████ + ██████████████ ██ ██ ██ ██ ██ ██ + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphic(1, drawQuietZones: false); - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] public void can_render_ascii_qrcode_with_custom_symbols() { - var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; + var targetCode = """ + + + + + + + + + XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX + XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX + XX XX XXXX XX XX + XX XX XXXX XX XX + XX XXXXXX XX XXXX XX XXXXXX XX + XX XXXXXX XX XXXX XX XXXXXX XX + XX XXXXXX XX XX XX XX XXXXXX XX + XX XXXXXX XX XX XX XX XXXXXX XX + XX XXXXXX XX XXXX XX XX XXXXXX XX + XX XXXXXX XX XXXX XX XX XXXXXX XX + XX XX XX XX XX XX + XX XX XX XX XX XX + XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX + XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX + XX XX + XX XX + XX XX XX XXXXXX XXXX XXXX XX + XX XX XX XXXXXX XXXX XXXX XX + XXXXXX XX XXXX XX XX XX XXXX + XXXXXX XX XXXX XX XX XX XXXX + XXXXXX XXXXXXXXXX XXXXXXXXXX + XXXXXX XXXXXXXXXX XXXXXXXXXX + XX XX XX XX XX XXXXXX XX XX + XX XX XX XX XX XXXXXX XX XX + XXXXXX XXXX XX XX XXXX XX XX + XXXXXX XXXX XX XX XXXX XX XX + XXXXXX XXXX XX XX + XXXXXX XXXX XX XX + XXXXXXXXXXXXXX XXXXXX XX + XXXXXXXXXXXXXX XXXXXX XX + XX XX XX XX XX + XX XX XX XX XX + XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX + XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX + XX XXXXXX XX XX XXXX XX XX XXXX + XX XXXXXX XX XX XXXX XX XX XXXX + XX XXXXXX XX XXXXXX XXXXXXXXXX + XX XXXXXX XX XXXXXX XXXXXXXXXX + XX XX XX XXXX XX XX XX + XX XX XX XXXX XX XX XX + XXXXXXXXXXXXXX XX XXXXXX XXXXXX + XXXXXXXXXXXXXX XX XXXXXX XXXXXX + + + + + + + + + """; //Create QR code var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("A", QRCodeGenerator.ECCLevel.Q); var asciiCode = new AsciiQRCode(data).GetGraphic(2, "X", " "); - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } [Fact] @@ -111,11 +276,70 @@ public void can_instantate_parameterless() [Fact] public void can_render_ascii_qrcode_from_helper() { - var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; + var targetCode = """ + + + + + + + + + XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX + XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX + XX XX XXXX XX XX + XX XX XXXX XX XX + XX XXXXXX XX XXXX XX XXXXXX XX + XX XXXXXX XX XXXX XX XXXXXX XX + XX XXXXXX XX XX XX XX XXXXXX XX + XX XXXXXX XX XX XX XX XXXXXX XX + XX XXXXXX XX XXXX XX XX XXXXXX XX + XX XXXXXX XX XXXX XX XX XXXXXX XX + XX XX XX XX XX XX + XX XX XX XX XX XX + XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX + XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX + XX XX + XX XX + XX XX XX XXXXXX XXXX XXXX XX + XX XX XX XXXXXX XXXX XXXX XX + XXXXXX XX XXXX XX XX XX XXXX + XXXXXX XX XXXX XX XX XX XXXX + XXXXXX XXXXXXXXXX XXXXXXXXXX + XXXXXX XXXXXXXXXX XXXXXXXXXX + XX XX XX XX XX XXXXXX XX XX + XX XX XX XX XX XXXXXX XX XX + XXXXXX XXXX XX XX XXXX XX XX + XXXXXX XXXX XX XX XXXX XX XX + XXXXXX XXXX XX XX + XXXXXX XXXX XX XX + XXXXXXXXXXXXXX XXXXXX XX + XXXXXXXXXXXXXX XXXXXX XX + XX XX XX XX XX + XX XX XX XX XX + XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX + XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX + XX XXXXXX XX XX XXXX XX XX XXXX + XX XXXXXX XX XX XXXX XX XX XXXX + XX XXXXXX XX XXXXXX XXXXXXXXXX + XX XXXXXX XX XXXXXX XXXXXXXXXX + XX XX XX XXXX XX XX XX + XX XX XX XXXX XX XX XX + XXXXXXXXXXXXXX XX XXXXXX XXXXXX + XXXXXXXXXXXXXX XX XXXXXX XXXXXX + + + + + + + + + """; //Create QR code var asciiCode = AsciiQRCodeHelper.GetQRCode("A", 2, "X", " ", QRCodeGenerator.ECCLevel.Q); - asciiCode.ShouldBe(targetCode); + asciiCode.ShouldBe(targetCode, StringCompareShould.IgnoreLineEndings); } } diff --git a/QRCoderTests/Base64QRCodeRendererTests.can_render_base64_qrcode_jpeg.approved.gif b/QRCoderTests/Base64QRCodeRendererTests.can_render_base64_qrcode_jpeg.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..5dbb6746d8e9c0e02945418ebc5809e010f5e915 GIT binary patch literal 2446 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 Mbg#SJO;7*;JM)37^#A|> literal 0 HcmV?d00001 diff --git a/QRCoderTests/Base64QRCodeRendererTests.cs b/QRCoderTests/Base64QRCodeRendererTests.cs index d66747b2..8826d150 100644 --- a/QRCoderTests/Base64QRCodeRendererTests.cs +++ b/QRCoderTests/Base64QRCodeRendererTests.cs @@ -1,6 +1,5 @@ using System; using System.Drawing; -using System.IO; using QRCoder; using Shouldly; using Xunit; @@ -53,15 +52,9 @@ public void can_render_base64_qrcode_transparent() [Fact] public void can_render_base64_qrcode_jpeg() { - var ms = new MemoryStream(); - using (var bitmap = new QRCode(_data).GetGraphic(5, Color.Black, Color.White, true)) - { - bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); - } - ms.Position = 0; - var jpgString = Convert.ToBase64String(ms.ToArray()); var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Black, Color.White, true, Base64QRCode.ImageType.Jpeg); - base64QRCode.ShouldBe(jpgString); + var data = Convert.FromBase64String(base64QRCode); + data.ShouldMatchApprovedImage(); } #endif } diff --git a/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode.approved.gif b/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..3290fe698636719d4fb6a6ed5f35d54f33fb777e GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_color_bytearray.approved.gif b/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_color_bytearray.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..c0f53cdc2610744b1aa8136a7a109b6598a581ad GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_drawing_color.approved.gif b/QRCoderTests/BitmapByteQRCodeRendererTests.can_render_bitmapbyte_qrcode_drawing_color.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..05e248ab6b3af96855a7c37362a202e6edc724ab GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/BitmapByteQRCodeRendererTests.cs b/QRCoderTests/BitmapByteQRCodeRendererTests.cs index ced9b3d3..763db05e 100644 --- a/QRCoderTests/BitmapByteQRCodeRendererTests.cs +++ b/QRCoderTests/BitmapByteQRCodeRendererTests.cs @@ -1,5 +1,4 @@ using QRCoder; -using QRCoderTests.Helpers; using Shouldly; using Xunit; @@ -13,9 +12,7 @@ public void can_render_bitmapbyte_qrcode() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new BitmapByteQRCode(data).GetGraphic(10); - - var result = HelperFunctions.ByteArrayToHash(bmp); - result.ShouldBe("2d262d074f5c436ad93025150392dd38"); + bmp.ShouldMatchApprovedImage(); } @@ -25,9 +22,7 @@ public void can_render_bitmapbyte_qrcode_color_bytearray() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new BitmapByteQRCode(data).GetGraphic(10, new byte[] { 30, 30, 30 }, new byte[] { 255, 0, 0 }); - - var result = HelperFunctions.ByteArrayToHash(bmp); - result.ShouldBe("1184507c7eb98f9ca76afd04313c41cb"); + bmp.ShouldMatchApprovedImage(); } [Fact] @@ -36,8 +31,6 @@ public void can_render_bitmapbyte_qrcode_drawing_color() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new BitmapByteQRCode(data).GetGraphic(10, "#e3e3e3", "#ffffff"); - - var result = HelperFunctions.ByteArrayToHash(bmp); - result.ShouldBe("40cd208fc46aa726d6e98a2028ffd2b7"); + bmp.ShouldMatchApprovedImage(); } } diff --git a/QRCoderTests/Helpers/HelperFunctions.cs b/QRCoderTests/Helpers/HelperFunctions.cs index 8fb41ddf..1d4efb2a 100644 --- a/QRCoderTests/Helpers/HelperFunctions.cs +++ b/QRCoderTests/Helpers/HelperFunctions.cs @@ -10,9 +10,7 @@ using System.Windows.Media.Imaging; #endif - namespace QRCoderTests.Helpers; - public static class HelperFunctions { @@ -96,12 +94,8 @@ public static Bitmap GetIconBitmap() { var assembly = Assembly.GetExecutingAssembly(); var resourceName = "QRCoderTests.assets.noun_software engineer_2909346.png"; - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream == null) - throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); - return new Bitmap(stream); - } + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); + return new Bitmap(stream); } /// @@ -111,16 +105,10 @@ public static byte[] GetIconBytes() { var assembly = Assembly.GetExecutingAssembly(); var resourceName = "QRCoderTests.assets.noun_software engineer_2909346.png"; - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream == null) - throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); - using (var memoryStream = new MemoryStream()) - { - stream.CopyTo(memoryStream); - return memoryStream.ToArray(); - } - } + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + return memoryStream.ToArray(); } /// @@ -130,14 +118,8 @@ public static string GetIconSvg() { var assembly = Assembly.GetExecutingAssembly(); var resourceName = "QRCoderTests.assets.noun_Scientist_2909361.svg"; - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream == null) - throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); - using (var reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } + using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException($"Embedded resource '{resourceName}' not found."); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); } } diff --git a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs new file mode 100644 index 00000000..4179843b --- /dev/null +++ b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs @@ -0,0 +1,123 @@ +#nullable enable + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using KGySoft.Drawing; +using KGySoft.Drawing.Imaging; + +namespace Shouldly; + +[ShouldlyMethods] +[EditorBrowsable(EditorBrowsableState.Never)] +public static partial class ShouldMatchApprovedExtensions +{ + public static void ShouldMatchApprovedImage(this byte[] imageBytes, string? discriminator = null, string? customMessage = null) + { + using var ms = new MemoryStream(imageBytes); + using var image = (Bitmap)Image.FromStream(ms); + image.ShouldMatchApproved(discriminator, customMessage); + } + public static void ShouldMatchApproved(this Bitmap image, string? discriminator = null, string? customMessage = null) + { + // encode to gif first for easier visual verification, and using a third party lib to avoid platform-specific compression differences + var readableBitmapData = image.GetReadableBitmapData(); + var ms = new MemoryStream(); + GifEncoder.EncodeImage(readableBitmapData, ms); + ms.ToArray().ShouldMatchApproved("gif", discriminator, customMessage); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ShouldMatchApproved(this byte[] actual, string fileExtension, string? discriminator = null, string? customMessage = null) + { + var stackTrace = new StackTrace(true); + + // Find the first frame that is NOT in a class marked with [ShouldlyMethods] + StackFrame? frame = null; + MethodBase? method = null; + + for (int i = 1; i < stackTrace.FrameCount; i++) + { + var currentFrame = stackTrace.GetFrame(i); + var currentMethod = currentFrame?.GetMethod(); + var declaringType = currentMethod?.DeclaringType; + + // Check if the declaring type has the ShouldlyMethods attribute + var hasShouldlyAttribute = declaringType?.GetCustomAttributes(typeof(ShouldlyMethodsAttribute), false).Any() ?? false; + + // If this method is NOT in a ShouldlyMethods class, use it + if (!hasShouldlyAttribute) + { + frame = currentFrame; + method = currentMethod; + break; + } + } + + if (frame == null) + throw new Exception("Unable to get stack frame information"); + if (method == null) + throw new Exception("Unable to get method information from stack frame"); + + var fileName = frame.GetFileName(); + if (string.IsNullOrEmpty(fileName)) + throw new Exception($"Source information not available, make sure you are compiling with full debug information. Method: {method.DeclaringType?.Name}.{method.Name}"); + + var outputFolder = Path.GetDirectoryName(fileName); + if (string.IsNullOrEmpty(outputFolder)) + throw new Exception($"Unable to determine output folder from file: {fileName}"); + + var className = method.DeclaringType?.Name ?? throw new Exception("Unable to get class name from method information"); + var testMethodName = method.Name; + var discriminatorPart = string.IsNullOrEmpty(discriminator) ? "" : $".{discriminator}"; + + var approvedFile = Path.Combine(outputFolder, $"{className}.{testMethodName}{discriminatorPart}.approved.{fileExtension}"); + var receivedFile = Path.Combine(outputFolder, $"{className}.{testMethodName}{discriminatorPart}.received.{fileExtension}"); + + File.WriteAllBytes(receivedFile, actual); + + if (!File.Exists(approvedFile)) + { + throw new ShouldAssertException($""" + Approval file {approvedFile} + does not exist + + To approve the received file, run: + + copy /Y "{receivedFile}" "{approvedFile}" + + """); + } + + var approvedFileContents = File.ReadAllBytes(approvedFile); + var receivedFileContents = File.ReadAllBytes(receivedFile); + + var contentsMatch = approvedFileContents.AsSpan().SequenceEqual(receivedFileContents.AsSpan()); + + if (!contentsMatch) + { + var baseMessage = $"Binary files do not match. Expected length: {approvedFileContents.Length}, Actual length: {receivedFileContents.Length}"; + var copyCommand = $""" + + + To approve the received file, run: + + copy /Y "{receivedFile}" "{approvedFile}" + + """; + + var message = customMessage != null + ? $"{customMessage}\n\n{baseMessage}{copyCommand}" + : $"{baseMessage}{copyCommand}"; + + throw new ShouldAssertException(message); + } + + File.Delete(receivedFile); + } +} diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..deb366a018ef53642786e0904a4badaeff2c22a5 GIT binary patch literal 1395 zcmV-(1&sPfNk%w1VWj}20Du4h{{R300RJKW6iY{WAX869R3Jx5Mj%CQV{c?-asVs< z0001`0Hpu`0{@JUsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~}=lag~{tpZa zhs2`sh)gP%%%<}RjY_9fD&UIEYPVc&Li^2zu@f+&i%xf0>UMj4g#+Ye@!5^{uJU~Q z!TjfE00==C(1*x|$VYca<`^eoxX8FiiPsqTs2LeCdFiIOsbMIW*HL-Y5$#LtMYtmcz%UQV*{BSHgN;^Cl%iBzm4842`Id`4rpp9F-ODx*y9D1$0 z*1+xSO%86k{#Kriwmsi14$D4%fAX1+e{YRJI$`hBWJ=KwUOQp~6*Qz*u;DagvjXbF z#;{(-ME~(N>RE`e;fCA-;!vY6v2t}Ov!+^yWqYOsI+0Y@ zvNy&0L`QdSTC-2lj)iLXmDj6qquDZS?`mMg0s&KV%8D^T$W+@VPKhBdGq4tpRaN(u zvNXvaG-JlQx6abXiu;OI{W`2F#}qk}P^xfkU6xpJN6qQ`uGHPb^#*Sp`M7Z1w3+)2 z9!~k{;?tc=w>{!IaqEPeXO&(Bb!g<_fov~7ev5h@4hm}#?`-LCwqUV>Dlw; zkNbGY(D$Eg2nsk9Q}vzIhh1-3MqPshdIup*Y}NM}cY^6hnlgVSl^2I0 zCbl04gRSUYfty+Q0*iYMHX(MIQRsq z>SmC74hrdvq|wM*43Bo1Dvz*fTB@TUa;oC3LxLI|t3~N5DX;DfS>|XYHd*MflK&Pv ztBy$u`|GiEmIf@8X75CKyz+Z3_nEQBA(w2c!-`gX$-s5aytAJ$tGX`~zDg`` zfY3&9G{T-`x?a;HcN;L%F(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcADYGc5{V8u~ zE9-LH{z6SR+zj8{FK`{>i_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk`TtrDww&Ce z8ZP6HAMQBdXxd9|)?@2DFXRZT9C7A$*F36`of8V$=b>|svFo|BeyHvy+gz*fU-MqN z)_cPqe7kSMsd|R6DsNNp&MMAt*leTzG3CHdUj6p-8Z4;L(Q|*g^_61p^ULG!>YDf2 zd)Rg6;ImKh`tLg(&GliY?>z3ZD-FQ^iEnuAd!F3#hd_PpZ+8XsA9tE}Kn2pOaApJA z_YzpS`@M>R^}C$=Hn=eDx$Sl&L>|$4N3v`A4Rj{t(bNz(LJu0wejqfS2rbpU8MbhO zD8yi}U_iFiU1oXHqF(v5!@mGdrg-w(T&~EL#1Jk~a`fw-%QQnpCOR7McUQ#W>Z;bn z*2QOxWGtf@&xpn}s&S2MY@-|B2*)_eagKDXqaE*v$2{tBk9_Q-AO8r*K#HIM06WqC B)TjUe literal 0 HcmV?d00001 diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_with_alpha.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_with_alpha.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..8df9617b89e7cc48833b56060a84db2561d9d5cf GIT binary patch literal 1403 zcmV->1%&!XNk%w1VWj}20Pq0-00930001HW6iY{WAX869R3Jx5Mj%CQV{c?-asVOu z1OWg50RSuj0001`0Hpu`0{?`MsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~} z=lag~{tpZahs2`sh)gP%%%<}RjY_9fYT$~^YPVc&Li^2zu@f+&i%xf0>UMj4g$Lwi z@!5^{uJU~Q!TjfE00=<{(1*x|$VYca<`^eoxX8FiiPsqTs2LeCdFiIOsbMIW*HL-Y5$#LtMYtmcz%UQV*{BSHgN;^Cl%iBzm4842`Id`4rpp9F- zODx*y9D1$0*1+xSO%86k{#Kriwmsi14$D4%fAX1+e{YRJI$`hBWJ=KwUOQp~6*Qz* zu;DagvjXbF#;{(-ME~(N>RE`e;fCA-;!vY6v2t}Ov!+^y zWqYOsI+0Y@vNy&0L`QdSTC-2lj)iLXmDj6qquDZS?`mMg0s&KV%8D^T$W+@VPKhBd zGq4tpRaN(uvNXvaG-JlQx6abXiu;OI{W`2F#}qk}P^xfkU6xpJN6qQ`uGHPb^#*Sp z`M7Z1w3+)29!~k{;?tc=w>{!IaqEPeXO&(Bb!g<_fov~7ev5h@4hm}#?`-L zCwqUV>Dlw;kNbGY(D$Eg2nsk9Q}vzIhh1-3MqPshdIup*Y}NM}cY^6h znlgVSl^2I0Cbl04gRSUYfty+Q0*iYMHX(MIQRsq>SmC74hrdvq|wM*43Bo1Dvz*fTB@TUa;oC3LxLI|t3~N5DX;DfS>|XY zHd*MflK&PvtBy$u`|GiEmIf@8X75CKyz+Z3_nEQBA(w2c!-`gX$-s5aytAJ$ ztGX`~zDg``fY3&9G{T-`x?a;HcN;L%F(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcA zDYGc5{V8u~E9-LH{z6SR+zj8{FK`{>i_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk z`TtrDww&Ce8ZP6HAMQBdXxd9|)?@2DFXRZT9C7A$*F36`of8V$=b>|svFo|BeyHvy z+gz*fU-MqN)_cPqe7kSMsd|R6DsNNp&MMAt*leTzG3CHdUj6p-8Z4;L(Q|*g^_61p z^ULG!>YDf2d)Rg6;ImKh`tLg(&GliY?>z3ZD-FQ^iEnuAd!F3#hd_PpZ+8XsA9tE} zKn2pOaApJA_YzpS`@M>R^}C$=Hn=eDx$Sl&L>|$4N3v`A4Rj{t(bNz(LJu0wejqfS z2rbpU8MbhOD8yi}U_iFiU1oXHqF(v5!@mGdrg-w(T&~EL#1Jk~a`fw-%QQnpCOR7M zcUQ#W>Z;bn*2QOxWGtf@&xpn}s&S2MY@-|B2*)_eagKDXqaE*v$2{tBk9_Q-AO8r* JK#HIM06P$7)n)(y literal 0 HcmV?d00001 diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_without_quietzones.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_color_without_quietzones.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..951c2c8425c9c1c1ad688e1155c27af916cded15 GIT binary patch literal 1130 zcmV-w1eN|&`wxanwM_>}Wm##7?h7{(OQ$M& z&hgHJxbX`FZo&f;^<{V1v#kYGB??gC)Q1p9h+<{t({$MIDTurEPkFYj17L>8@+ulnoWKY8J7!D_b;UcPw;yA|B# z?U}@dv)DOYWsw~~bN>_(HsnaJ8ACUMKCaog#}~;^0aT)K0>W`^ZeNL;woQidzQjk`&dL;+*;>E9I#c~a+ky{}@ zPS?79%N8x%xpZl6t&2A=P?$CrRRhA#DJLG89PbLa8>v(M4hZB7kee)#glsj#9KUK;UFyQkkz zv)&j#oxru<(f`h`TuRMRcZPnBFR35g?- zIfA4YlG||@V~un<8Df0oorqvvR5o~HmR1txBXY)-hLeQjNm-_f0(nVbl}ci#Ad_-h z*aLTBrp8{0XBJ53e37Wx9Ect(U0%aB8mxoJLaA(~WF9Nnv&HUe;*Vubi)XH6i6?BJmX6D4xqyO*Zm@uv z%Wk`9ItwqBZWiTbv)9?0uD+_~>8O2a!sqXbhmqUn!2Kf3@1g$|%w>88O9}Ciq4s&D zw`_*XFL)hi+~Kvvmdfy{8qV9T!XCF5FNg{+%jtbA+p8_C+s5pl!%L>T9?Tz7)-%WZ z68m$VLGwH`wC4gFrKg}`j3LlT(|GHE)Mj_IjT9fe?y6%xt@PGiU+Of-^#Z);*Ot17 z8`@l#9d(@DYMVB%RFf??xBrqHx6KLL4JY4T1D>_WI+@*azHi?IjW?04ZXzx;UL wgegxgxXgMRJ~4cm<7#c>P;cDdxRYPLC%~V(7dq-Ot1Pd*fq#s0tOWo7J9AbvTL1t6 literal 0 HcmV?d00001 diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_drawing_color.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_drawing_color.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..deb366a018ef53642786e0904a4badaeff2c22a5 GIT binary patch literal 1395 zcmV-(1&sPfNk%w1VWj}20Du4h{{R300RJKW6iY{WAX869R3Jx5Mj%CQV{c?-asVs< z0001`0Hpu`0{@JUsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~}=lag~{tpZa zhs2`sh)gP%%%<}RjY_9fD&UIEYPVc&Li^2zu@f+&i%xf0>UMj4g#+Ye@!5^{uJU~Q z!TjfE00==C(1*x|$VYca<`^eoxX8FiiPsqTs2LeCdFiIOsbMIW*HL-Y5$#LtMYtmcz%UQV*{BSHgN;^Cl%iBzm4842`Id`4rpp9F-ODx*y9D1$0 z*1+xSO%86k{#Kriwmsi14$D4%fAX1+e{YRJI$`hBWJ=KwUOQp~6*Qz*u;DagvjXbF z#;{(-ME~(N>RE`e;fCA-;!vY6v2t}Ov!+^yWqYOsI+0Y@ zvNy&0L`QdSTC-2lj)iLXmDj6qquDZS?`mMg0s&KV%8D^T$W+@VPKhBdGq4tpRaN(u zvNXvaG-JlQx6abXiu;OI{W`2F#}qk}P^xfkU6xpJN6qQ`uGHPb^#*Sp`M7Z1w3+)2 z9!~k{;?tc=w>{!IaqEPeXO&(Bb!g<_fov~7ev5h@4hm}#?`-LCwqUV>Dlw; zkNbGY(D$Eg2nsk9Q}vzIhh1-3MqPshdIup*Y}NM}cY^6hnlgVSl^2I0 zCbl04gRSUYfty+Q0*iYMHX(MIQRsq z>SmC74hrdvq|wM*43Bo1Dvz*fTB@TUa;oC3LxLI|t3~N5DX;DfS>|XYHd*MflK&Pv ztBy$u`|GiEmIf@8X75CKyz+Z3_nEQBA(w2c!-`gX$-s5aytAJ$tGX`~zDg`` zfY3&9G{T-`x?a;HcN;L%F(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcADYGc5{V8u~ zE9-LH{z6SR+zj8{FK`{>i_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk`TtrDww&Ce z8ZP6HAMQBdXxd9|)?@2DFXRZT9C7A$*F36`of8V$=b>|svFo|BeyHvy+gz*fU-MqN z)_cPqe7kSMsd|R6DsNNp&MMAt*leTzG3CHdUj6p-8Z4;L(Q|*g^_61p^ULG!>YDf2 zd)Rg6;ImKh`tLg(&GliY?>z3ZD-FQ^iEnuAd!F3#hd_PpZ+8XsA9tE}Kn2pOaApJA z_YzpS`@M>R^}C$=Hn=eDx$Sl&L>|$4N3v`A4Rj{t(bNz(LJu0wejqfS2rbpU8MbhO zD8yi}U_iFiU1oXHqF(v5!@mGdrg-w(T&~EL#1Jk~a`fw-%QQnpCOR7McUQ#W>Z;bn z*2QOxWGtf@&xpn}s&S2MY@-|B2*)_eagKDXqaE*v$2{tBk9_Q-AO8r*K#HIM06WqC B)TjUe literal 0 HcmV?d00001 diff --git a/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_from_helper.approved.gif b/QRCoderTests/PngByteQRCodeRendererTests.can_render_pngbyte_qrcode_from_helper.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..eee9f4409bf531dda404b2963b6cfaccf18e688b GIT binary patch literal 3380 zcmV-44a@RJNk%w1VM+l?0e}Di00030|NkNW6iY{WAX869R3Jx5Mj%CQV{c?-asVs< z0000=0ZIV?0{@JUsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~}=lag~{tpZa zhs2`sh)gP%%%<}RjY_A~s`ZM^YPa03_X`e-$K-YS= z{|^`_I7nD%c!-#&xX9S(_y`#(IZ0V*d5M{+xyjk-`3V{-I!an`U)E> zyXp{I3w!%2V%sq5`wJXQ6--=goa>6*s?55J+iM&x{VX+YeGR}&3$4qY9Ky?hJ$^hz zeh#dys-DW84Nkp)J})~=f4}UVFCMQD{_)QrzJcw`5gh0)p}uDDy74=B&mKg23Lk#6 zxDQ~z1pgBw8tjO!qP&d=0(LN25@g7aD#wWw=x}6A4=4|`JSXwywU-=8+VrWRrU9KU zS!R?W^Z`g4Nm(>a@>E4snGl%*Z86nI))QM(PP95RM%N)@Nt6w+w8mLyVV$gPc(FoS z8gD_=?QxgpRd@5i#zndpYGA2@sUGxuRBU0bi@7%TT2<}i5_xx2=5TiIjLRE1cgNfQq&=N)Ip^CoS|2K%x$!8e!^4z4TsQY(v4vkJ$J1?e?BbW7CoZ?U-tTD5 z&;Lf>M0xbxhZ}*-<;Pxd1^$Phf#X3TAbh(~SD!Sm>37n65@vTBYZX3Mp>_$bM@)MQ zrgxx&_B|0{gAAVdo{JKIxF3Mv4fvvm5Nar6j4Om|Vv_Rlmt2<9Z5bw%N1lmcniTFxrhjf0DU6mwa>(Y0 zW|GGvmMXr^pkvzjWss4gdg`aC?zt+cj8+FLsk`Dj!KO;F%4w#p3JV%(I{yYr zY^;eoTP2;*nmTHqi?%B2vYIw~rM3|WE3LQ1x_T_Prv=gNlAg|r;H2%=%5JXi#>=9$ z>Z)2QiSPz%uBEc(TWPOyV!JNCx_;ZQz?Y7jt(fH=%y7XoHXE+P5!0HE!~xnnO`+$4 zORc!jitH=Fygpm<#lTJs@vtqY+p)$>z-*z+BU|k9yawz1tGxU6TrYo4om;W7W^qie z3&~0*^a`MX44uEv4&5{oOY1u=*1u4#FsxipEm+m45FIkkCX!5c)Lw_ZHeq5@0JV2r zgY9>t(NgP;Lg#5CQ{)%l2&Y@&(T?yd9qA~^MmhfLe1=R|9zBUk1_G~fn%e>vH|aYssxp*n zOq?aXaLP|M&yR(47}3j3G?g1Q z=tDIbM~PaonGMCmMb8*gjgEAe9i78T`zXl_5!8)xcqt;kqEJG9v!yltXg)n6Qh$Ns2vSxu6_!ptsu70@(REf-4@PBZmj)SD zQ3{f*T{SCI?HR@_!lA4?)d^OAI>krwfv!V+>sp@|)jIH%rFKPYR@GV35w^9ioib}$ z^Ey^jG7_BL?CWB!npmmI6^>e+Y)hNyxS9I$3~`lWJVpD%!wQw2*aSmqDSJ-FW;UCl zp{7`s$4$>7Hnx_ns!Sg_*xW8vx2vV-XGh7=-Fk_u!~bnAa0NO~%-B|~>?|j7sVmEZ zMz=H5g)DBZ>s;8L_PLt{Yapxp&g*)&nxI84F@4KP(n5EueH$Zm?R6 zIbZkQpuIT!?=<^cKe7b4z=q`lfzgYu1^c(b`PD9c>y}-bQW(Gp{%++uTulQD*TMq+ zTV)AbT$n~Sk{R}Jgh9+qjHWofFb1UfYRp?1&z8mze(HTUygD@6jJZAbDuW$sOXUu^ z3!$amV=oNY%O1GMZv8QmO#@%r5;?&)b+T%Aj1_r8nFmc~-IGiF<@Me+wGI}r@Xown z>1MgLO^tJM^@-Opvv#|5-m;5*d}P_S62qgdW&fM){N9+Nl&AF-vY|P*RkHSrb#{LqMoim;JwE zW4E)n1GlW3on|z=7J$hnDvE*1>o=^M-D(E&n|JMod9xeZ+D_@WEvjB=qqec}&Ue5~ zeeT|p7}0gcHoe=eZ+QQEt_KIUuoLZXYXe-|2)uZ|p@{D`M7-n7R(OlOE%J)1kKgy^ z_9@J1ZjH~{fRnEE zc`f<)U-ljIS)cn~7Z>?M=RNS}nT%^*u*yy1)ASg4ey`uWvf( z+g|(PPkh-uPyOO=fBePgH`En8egCP;{`rC4zf#Gs183)R?uUG|$6cqVe7NUupvQl! z_giL`Q}EYmQnqxZms3b(et73=1DAajcz<06fkD-Q__u(^#d{PuY=tF)gNJ$>NO|@5 zd4FbP6Bc_KNNz7EgG4lYDrkKp_+cg3bOShiT?c|haW?1&c`nF+Czx%-2XZSYfhGrd z4>*Md$b;aAatf$~CJ2DH)_@_{gDkjwKsaNRHgds*eP;JnHW+DX7iBo+gl!0GahG`~ zrdecYZ_~Df+eU|K$bl9}e^r=*W!Q3k2xty?hbM=G&c|`R2a0ltgNG-DtjK<+2z@OWijXL264r@-_=&wX zi-d@RLbzwO_=lu-h^44>UZ;bT$B8*;dApd0O-O{1m~CR`c8|!5&Zu_4h=+h!Vbs`+ zh&YHo2#wx|ae~NW+^BV4_#-ue(}h0AINIih>hx~kL1XXqNt3r zsEX~_cs1yOF&L2i7=?nDkMpR0#JFnGSdRZlg$K!Aa3zl+CVBZcgn>wl7MYF)=vu<} zex&G+kZ6ynsCqC6g(TUL2sm~MDUwiS;l3_TECHq;1Cz+HZwv<@dlOXwV?&ym-d5$5ekt4~G)p(P}IDuU$lZ*F^ z8Ay{qX^LU_gO_)f%@>9{czo}edc`1%&!XNk%w1VWj}20Pq0-00930001HW6iY{WAX869R3Jx5Mj%CQV{c?-asVOu z1OWg50RSuj0001`0Hpu`0{?`MsmtvTqnxzbi?iOm`wxcVNS5Y_rs~SJ?hD8AOxN~} z=lag~{tpZahs2`sh)gP%%%<}RjY_9fYT$~^YPVc&Li^2zu@f+&i%xf0>UMj4g$Lwi z@!5^{uJU~Q!TjfE00=<{(1*x|$VYca<`^eoxX8FiiPsqTs2LeCdFiIOsbMIW*HL-Y5$#LtMYtmcz%UQV*{BSHgN;^Cl%iBzm4842`Id`4rpp9F- zODx*y9D1$0*1+xSO%86k{#Kriwmsi14$D4%fAX1+e{YRJI$`hBWJ=KwUOQp~6*Qz* zu;DagvjXbF#;{(-ME~(N>RE`e;fCA-;!vY6v2t}Ov!+^y zWqYOsI+0Y@vNy&0L`QdSTC-2lj)iLXmDj6qquDZS?`mMg0s&KV%8D^T$W+@VPKhBd zGq4tpRaN(uvNXvaG-JlQx6abXiu;OI{W`2F#}qk}P^xfkU6xpJN6qQ`uGHPb^#*Sp z`M7Z1w3+)29!~k{;?tc=w>{!IaqEPeXO&(Bb!g<_fov~7ev5h@4hm}#?`-L zCwqUV>Dlw;kNbGY(D$Eg2nsk9Q}vzIhh1-3MqPshdIup*Y}NM}cY^6h znlgVSl^2I0Cbl04gRSUYfty+Q0*iYMHX(MIQRsq>SmC74hrdvq|wM*43Bo1Dvz*fTB@TUa;oC3LxLI|t3~N5DX;DfS>|XY zHd*MflK&PvtBy$u`|GiEmIf@8X75CKyz+Z3_nEQBA(w2c!-`gX$-s5aytAJ$ ztGX`~zDg``fY3&9G{T-`x?a;HcN;L%F(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcA zDYGc5{V8u~E9-LH{z6SR+zj8{FK`{>i_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk z`TtrDww&Ce8ZP6HAMQBdXxd9|)?@2DFXRZT9C7A$*F36`of8V$=b>|svFo|BeyHvy z+gz*fU-MqN)_cPqe7kSMsd|R6DsNNp&MMAt*leTzG3CHdUj6p-8Z4;L(Q|*g^_61p z^ULG!>YDf2d)Rg6;ImKh`tLg(&GliY?>z3ZD-FQ^iEnuAd!F3#hd_PpZ+8XsA9tE} zKn2pOaApJA_YzpS`@M>R^}C$=Hn=eDx$Sl&L>|$4N3v`A4Rj{t(bNz(LJu0wejqfS z2rbpU8MbhOD8yi}U_iFiU1oXHqF(v5!@mGdrg-w(T&~EL#1Jk~a`fw-%QQnpCOR7M zcUQ#W>Z;bn*2QOxWGtf@&xpn}s&S2MY@-|B2*)_eagKDXqaE*v$2{tBk9_Q-AO8r* JK#HIM06P$7)n)(y literal 0 HcmV?d00001 diff --git a/QRCoderTests/PngByteQRCodeRendererTests.cs b/QRCoderTests/PngByteQRCodeRendererTests.cs index f2850c30..269bbed2 100644 --- a/QRCoderTests/PngByteQRCodeRendererTests.cs +++ b/QRCoderTests/PngByteQRCodeRendererTests.cs @@ -1,11 +1,7 @@ -using Xunit; +using System.Drawing; using QRCoder; using Shouldly; -using QRCoderTests.Helpers; -#if !NETCOREAPP1_1 -using System.Drawing; -using System.IO; -#endif +using Xunit; namespace QRCoderTests; @@ -27,16 +23,7 @@ public void can_render_pngbyte_qrcode_blackwhite() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("1fc35c3bea6fad47427143ce716c83b8"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("0cfc8a8d552ade875190d8e9f5c1e1bf"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } [Fact] @@ -46,19 +33,9 @@ public void can_render_pngbyte_qrcode_color() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("88d394b2405499869feb69b81593e703"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } -#if !NETCOREAPP1_1 [Fact] public void can_render_pngbyte_qrcode_drawing_color() { @@ -66,21 +43,8 @@ public void can_render_pngbyte_qrcode_drawing_color() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, Color.Red, Color.Blue); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7"); -#else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("88d394b2405499869feb69b81593e703"); - } -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } -#endif - [Fact] public void can_render_pngbyte_qrcode_color_with_alpha() @@ -89,16 +53,7 @@ public void can_render_pngbyte_qrcode_color_with_alpha() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } [Fact] @@ -108,17 +63,7 @@ public void can_render_pngbyte_qrcode_color_without_quietzones() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, false); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("07f760b3eb54901840b094d31e299713"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - bmp.MakeTransparent(Color.Transparent); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("825a6469f89bf9e3d7318a5390d5ba7f"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } [Fact] @@ -134,16 +79,7 @@ public void can_render_pngbyte_qrcode_from_helper() { //Create QR code var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L, 10); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("c562388f4f3cf13a299b469a3e3b852f"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("a2ea116068eb516a7c210b2541e99348"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } [Fact] @@ -151,15 +87,6 @@ public void can_render_pngbyte_qrcode_from_helper_2() { //Create QR code var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", 5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, QRCodeGenerator.ECCLevel.L); - -#if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); -#else - using var mStream = new MemoryStream(pngCodeGfx); - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); -#endif + pngCodeGfx.ShouldMatchApprovedImage(); } } diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt new file mode 100644 index 00000000..4d1872cf --- /dev/null +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt @@ -0,0 +1,121 @@ +%!PS-Adobe-3.0 +%%Creator: QRCoder.NET +%%Title: QRCode +%%DocumentData: Clean7Bit +%%Origin: 0 +%%DocumentMedia: Default 165 165 0 () () +%%BoundingBox: 0 0 165 165 +%%LanguageLevel: 2 +%%Pages: 1 +%%Page: 1 1 +%%EndComments +%%BeginConstants +/sz 165 def +/sc 5 def +%%EndConstants +%%BeginFeature: *PageSize Default +<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%BeginFunctions +/csquare { + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill +} def +/f { + 1 0 0 csquare + 1 0 translate +} def +/b { + 1 0 translate +} def +/background { + 0 0 1 csquare +} def +/nl { + -33 -1 translate +} def +%%EndFunctions +%%BeginBody +0 0 moveto +gsave +sz sz scale +background +grestore +gsave +sc sc scale +0 33 1 sub translate +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b f f f f f f f b f b f b f b b b f b f f f f f f f b b b b +nl +b b b b f b b b b b f b b f f f f b b b b b f b b b b b f b b b b +nl +b b b b f b f f f b f b f f f b b f f b b b f b f f f b f b b b b +nl +b b b b f b f f f b f b f f f b f f f f f b f b f f f b f b b b b +nl +b b b b f b f f f b f b f b b f f b b f f b f b f f f b f b b b b +nl +b b b b f b b b b b f b b b f f b b b b f b f b b b b b f b b b b +nl +b b b b f f f f f f f b f b f b f b f b f b f f f f f f f b b b b +nl +b b b b b b b b b b b b b b f b f b b b b b b b b b b b b b b b b +nl +b b b b f f f f b b f b f b f f b b f b f f b b f f f b f b b b b +nl +b b b b f f b f f f b f f b f b f b f b b b b b b f f b b b b b b +nl +b b b b b f f f f f f b b f f f f b b b f f b f f f b b b b b b b +nl +b b b b b b b f b f b b f f f f f f b f f b b f b f f b b b b b b +nl +b b b b f b f f b f f f b f f f b f b b b f f b f b f b b b b b b +nl +b b b b b f f f b f b b b b b b b b f f f b f f f b b f f b b b b +nl +b b b b b f b b b f f f f f b b f f b b b b b f b f b f b b b b b +nl +b b b b f b f f b f b f f f f f f b f b b f f b b f b b f b b b b +nl +b b b b b b f b f f f f f f f f b f f b f f f f f b f b b b b b b +nl +b b b b b b b b b b b b f f b f b f b b f b b b f b f f b b b b b +nl +b b b b f f f f f f f b b b b b b b b b f b f b f f b f f b b b b +nl +b b b b f b b b b b f b b f f b b b f f f b b b f f b b f b b b b +nl +b b b b f b f f f b f b b b f b f b b b f f f f f b b b f b b b b +nl +b b b b f b f f f b f b f b f f f b f b b f f b f f f f f b b b b +nl +b b b b f b f f f b f b f b b f b b f b f b f b f f b f b b b b b +nl +b b b b f b b b b b f b f f f f b f f b b f b b f f f b b b b b b +nl +b b b b f f f f f f f b f b b b b f b b b f f b b b f f f b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +%%EndBody +grestore +showpage +%%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt new file mode 100644 index 00000000..370c3675 --- /dev/null +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt @@ -0,0 +1,121 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: QRCoder.NET +%%Title: QRCode +%%DocumentData: Clean7Bit +%%Origin: 0 +%%DocumentMedia: Default 165 165 0 () () +%%BoundingBox: 0 0 165 165 +%%LanguageLevel: 2 +%%Pages: 1 +%%Page: 1 1 +%%EndComments +%%BeginConstants +/sz 165 def +/sc 5 def +%%EndConstants +%%BeginFeature: *PageSize Default +<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%BeginFunctions +/csquare { + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill +} def +/f { + 0 0 0 csquare + 1 0 translate +} def +/b { + 1 0 translate +} def +/background { + 1 1 1 csquare +} def +/nl { + -33 -1 translate +} def +%%EndFunctions +%%BeginBody +0 0 moveto +gsave +sz sz scale +background +grestore +gsave +sc sc scale +0 33 1 sub translate +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b f f f f f f f b f b f b f b b b f b f f f f f f f b b b b +nl +b b b b f b b b b b f b b f f f f b b b b b f b b b b b f b b b b +nl +b b b b f b f f f b f b f f f b b f f b b b f b f f f b f b b b b +nl +b b b b f b f f f b f b f f f b f f f f f b f b f f f b f b b b b +nl +b b b b f b f f f b f b f b b f f b b f f b f b f f f b f b b b b +nl +b b b b f b b b b b f b b b f f b b b b f b f b b b b b f b b b b +nl +b b b b f f f f f f f b f b f b f b f b f b f f f f f f f b b b b +nl +b b b b b b b b b b b b b b f b f b b b b b b b b b b b b b b b b +nl +b b b b f f f f b b f b f b f f b b f b f f b b f f f b f b b b b +nl +b b b b f f b f f f b f f b f b f b f b b b b b b f f b b b b b b +nl +b b b b b f f f f f f b b f f f f b b b f f b f f f b b b b b b b +nl +b b b b b b b f b f b b f f f f f f b f f b b f b f f b b b b b b +nl +b b b b f b f f b f f f b f f f b f b b b f f b f b f b b b b b b +nl +b b b b b f f f b f b b b b b b b b f f f b f f f b b f f b b b b +nl +b b b b b f b b b f f f f f b b f f b b b b b f b f b f b b b b b +nl +b b b b f b f f b f b f f f f f f b f b b f f b b f b b f b b b b +nl +b b b b b b f b f f f f f f f f b f f b f f f f f b f b b b b b b +nl +b b b b b b b b b b b b f f b f b f b b f b b b f b f f b b b b b +nl +b b b b f f f f f f f b b b b b b b b b f b f b f f b f f b b b b +nl +b b b b f b b b b b f b b f f b b b f f f b b b f f b b f b b b b +nl +b b b b f b f f f b f b b b f b f b b b f f f f f b b b f b b b b +nl +b b b b f b f f f b f b f b f f f b f b b f f b f f f f f b b b b +nl +b b b b f b f f f b f b f b b f b b f b f b f b f f b f b b b b b +nl +b b b b f b b b b b f b f f f f b f f b b f b b f f f b b b b b b +nl +b b b b f f f f f f f b f b b b b f b b b f f b b b f f f b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +%%EndBody +grestore +showpage +%%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt new file mode 100644 index 00000000..93c83ee7 --- /dev/null +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt @@ -0,0 +1,121 @@ +%!PS-Adobe-3.0 +%%Creator: QRCoder.NET +%%Title: QRCode +%%DocumentData: Clean7Bit +%%Origin: 0 +%%DocumentMedia: Default 165 165 0 () () +%%BoundingBox: 0 0 165 165 +%%LanguageLevel: 2 +%%Pages: 1 +%%Page: 1 1 +%%EndComments +%%BeginConstants +/sz 165 def +/sc 5 def +%%EndConstants +%%BeginFeature: *PageSize Default +<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%BeginFunctions +/csquare { + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill +} def +/f { + 0 0 0 csquare + 1 0 translate +} def +/b { + 1 0 translate +} def +/background { + 1 1 1 csquare +} def +/nl { + -33 -1 translate +} def +%%EndFunctions +%%BeginBody +0 0 moveto +gsave +sz sz scale +background +grestore +gsave +sc sc scale +0 33 1 sub translate +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b f f f f f f f b f b f b f b b b f b f f f f f f f b b b b +nl +b b b b f b b b b b f b b f f f f b b b b b f b b b b b f b b b b +nl +b b b b f b f f f b f b f f f b b f f b b b f b f f f b f b b b b +nl +b b b b f b f f f b f b f f f b f f f f f b f b f f f b f b b b b +nl +b b b b f b f f f b f b f b b f f b b f f b f b f f f b f b b b b +nl +b b b b f b b b b b f b b b f f b b b b f b f b b b b b f b b b b +nl +b b b b f f f f f f f b f b f b f b f b f b f f f f f f f b b b b +nl +b b b b b b b b b b b b b b f b f b b b b b b b b b b b b b b b b +nl +b b b b f f f f b b f b f b f f b b f b f f b b f f f b f b b b b +nl +b b b b f f b f f f b f f b f b f b f b b b b b b f f b b b b b b +nl +b b b b b f f f f f f b b f f f f b b b f f b f f f b b b b b b b +nl +b b b b b b b f b f b b f f f f f f b f f b b f b f f b b b b b b +nl +b b b b f b f f b f f f b f f f b f b b b f f b f b f b b b b b b +nl +b b b b b f f f b f b b b b b b b b f f f b f f f b b f f b b b b +nl +b b b b b f b b b f f f f f b b f f b b b b b f b f b f b b b b b +nl +b b b b f b f f b f b f f f f f f b f b b f f b b f b b f b b b b +nl +b b b b b b f b f f f f f f f f b f f b f f f f f b f b b b b b b +nl +b b b b b b b b b b b b f f b f b f b b f b b b f b f f b b b b b +nl +b b b b f f f f f f f b b b b b b b b b f b f b f f b f f b b b b +nl +b b b b f b b b b b f b b f f b b b f f f b b b f f b b f b b b b +nl +b b b b f b f f f b f b b b f b f b b b f f f f f b b b f b b b b +nl +b b b b f b f f f b f b f b f f f b f b b f f b f f f f f b b b b +nl +b b b b f b f f f b f b f b b f b b f b f b f b f f b f b b b b b +nl +b b b b f b b b b b f b f f f f b f f b b f b b f f f b b b b b b +nl +b b b b f f f f f f f b f b b b b f b b b f f b b b f f f b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +%%EndBody +grestore +showpage +%%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt new file mode 100644 index 00000000..41dde38f --- /dev/null +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt @@ -0,0 +1,121 @@ +%!PS-Adobe-3.0 +%%Creator: QRCoder.NET +%%Title: QRCode +%%DocumentData: Clean7Bit +%%Origin: 0 +%%DocumentMedia: Default 33 33 0 () () +%%BoundingBox: 0 0 33 33 +%%LanguageLevel: 2 +%%Pages: 1 +%%Page: 1 1 +%%EndComments +%%BeginConstants +/sz 33 def +/sc 1 def +%%EndConstants +%%BeginFeature: *PageSize Default +<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%BeginFunctions +/csquare { + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill +} def +/f { + 0 0 0 csquare + 1 0 translate +} def +/b { + 1 0 translate +} def +/background { + 1 1 1 csquare +} def +/nl { + -33 -1 translate +} def +%%EndFunctions +%%BeginBody +0 0 moveto +gsave +sz sz scale +background +grestore +gsave +sc sc scale +0 33 1 sub translate +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b f f f f f f f b f b f b f b b b f b f f f f f f f b b b b +nl +b b b b f b b b b b f b b f f f f b b b b b f b b b b b f b b b b +nl +b b b b f b f f f b f b f f f b b f f b b b f b f f f b f b b b b +nl +b b b b f b f f f b f b f f f b f f f f f b f b f f f b f b b b b +nl +b b b b f b f f f b f b f b b f f b b f f b f b f f f b f b b b b +nl +b b b b f b b b b b f b b b f f b b b b f b f b b b b b f b b b b +nl +b b b b f f f f f f f b f b f b f b f b f b f f f f f f f b b b b +nl +b b b b b b b b b b b b b b f b f b b b b b b b b b b b b b b b b +nl +b b b b f f f f b b f b f b f f b b f b f f b b f f f b f b b b b +nl +b b b b f f b f f f b f f b f b f b f b b b b b b f f b b b b b b +nl +b b b b b f f f f f f b b f f f f b b b f f b f f f b b b b b b b +nl +b b b b b b b f b f b b f f f f f f b f f b b f b f f b b b b b b +nl +b b b b f b f f b f f f b f f f b f b b b f f b f b f b b b b b b +nl +b b b b b f f f b f b b b b b b b b f f f b f f f b b f f b b b b +nl +b b b b b f b b b f f f f f b b f f b b b b b f b f b f b b b b b +nl +b b b b f b f f b f b f f f f f f b f b b f f b b f b b f b b b b +nl +b b b b b b f b f f f f f f f f b f f b f f f f f b f b b b b b b +nl +b b b b b b b b b b b b f f b f b f b b f b b b f b f f b b b b b +nl +b b b b f f f f f f f b b b b b b b b b f b f b f f b f f b b b b +nl +b b b b f b b b b b f b b f f b b b f f f b b b f f b b f b b b b +nl +b b b b f b f f f b f b b b f b f b b b f f f f f b b b f b b b b +nl +b b b b f b f f f b f b f b f f f b f b b f f b f f f f f b b b b +nl +b b b b f b f f f b f b f b b f b b f b f b f b f f b f b b b b b +nl +b b b b f b b b b b f b f f f f b f f b b f b b f f f b b b b b b +nl +b b b b f f f f f f f b f b b b b f b b b f f b b b f f f b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +nl +b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b +%%EndBody +grestore +showpage +%%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt new file mode 100644 index 00000000..006f2148 --- /dev/null +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt @@ -0,0 +1,105 @@ +%!PS-Adobe-3.0 +%%Creator: QRCoder.NET +%%Title: QRCode +%%DocumentData: Clean7Bit +%%Origin: 0 +%%DocumentMedia: Default 50 50 0 () () +%%BoundingBox: 0 0 50 50 +%%LanguageLevel: 2 +%%Pages: 1 +%%Page: 1 1 +%%EndComments +%%BeginConstants +/sz 50 def +/sc 2 def +%%EndConstants +%%BeginFeature: *PageSize Default +<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice +%%EndFeature +%%BeginFunctions +/csquare { + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill +} def +/f { + 0 0 0 csquare + 1 0 translate +} def +/b { + 1 0 translate +} def +/background { + 1 1 1 csquare +} def +/nl { + -25 -1 translate +} def +%%EndFunctions +%%BeginBody +0 0 moveto +gsave +sz sz scale +background +grestore +gsave +sc sc scale +0 25 1 sub translate +f f f f f f f b f b f b f b b b f b f f f f f f f +nl +f b b b b b f b b f f f f b b b b b f b b b b b f +nl +f b f f f b f b f f f b b f f b b b f b f f f b f +nl +f b f f f b f b f f f b f f f f f b f b f f f b f +nl +f b f f f b f b f b b f f b b f f b f b f f f b f +nl +f b b b b b f b b b f f b b b b f b f b b b b b f +nl +f f f f f f f b f b f b f b f b f b f f f f f f f +nl +b b b b b b b b b b f b f b b b b b b b b b b b b +nl +f f f f b b f b f b f f b b f b f f b b f f f b f +nl +f f b f f f b f f b f b f b f b b b b b b f f b b +nl +b f f f f f f b b f f f f b b b f f b f f f b b b +nl +b b b f b f b b f f f f f f b f f b b f b f f b b +nl +f b f f b f f f b f f f b f b b b f f b f b f b b +nl +b f f f b f b b b b b b b b f f f b f f f b b f f +nl +b f b b b f f f f f b b f f b b b b b f b f b f b +nl +f b f f b f b f f f f f f b f b b f f b b f b b f +nl +b b f b f f f f f f f f b f f b f f f f f b f b b +nl +b b b b b b b b f f b f b f b b f b b b f b f f b +nl +f f f f f f f b b b b b b b b b f b f b f f b f f +nl +f b b b b b f b b f f b b b f f f b b b f f b b f +nl +f b f f f b f b b b f b f b b b f f f f f b b b f +nl +f b f f f b f b f b f f f b f b b f f b f f f f f +nl +f b f f f b f b f b b f b b f b f b f b f f b f b +nl +f b b b b b f b f f f f b f f b b f b b f f f b b +nl +f f f f f f f b f b b b b f b b b f f b b b f f f +%%EndBody +grestore +showpage +%%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.cs b/QRCoderTests/PostscriptQRCodeRendererTests.cs index 32c5ea00..8560b08a 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.cs +++ b/QRCoderTests/PostscriptQRCodeRendererTests.cs @@ -1,10 +1,6 @@ -#if !NETCOREAPP1_1 -using System; using System.Drawing; -using System.IO; using System.Text.RegularExpressions; using QRCoder; -using QRCoderTests.Helpers; using Shouldly; using Xunit; @@ -19,9 +15,7 @@ public void can_render_postscript_qrcode_simple() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var ps = new PostscriptQRCode(data).GetGraphic(5); - - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("06b90d1e64bf022a248453e5f91101a0"); + RemoveCreationDate(ps).ShouldMatchApproved(x => x.NoDiff()); } [Fact] @@ -31,9 +25,7 @@ public void can_render_postscript_qrcode_eps() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var ps = new PostscriptQRCode(data).GetGraphic(5, true); - - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("50f6152cdb0b685595d80e7888712d3b"); + RemoveCreationDate(ps).ShouldMatchApproved(x => x.NoDiff()); } [Fact] @@ -43,9 +35,7 @@ public void can_render_postscript_qrcode_size() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var ps = new PostscriptQRCode(data).GetGraphic(new Size(33, 33)); - - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("49c7faaafef312eb4b6ea1fec195e63d"); + RemoveCreationDate(ps).ShouldMatchApproved(x => x.NoDiff()); } [Fact] @@ -55,9 +45,7 @@ public void can_render_postscript_qrcode_size_no_quiet_zones() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var ps = new PostscriptQRCode(data).GetGraphic(new Size(50, 50), false); - - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("9bfa0468e125d9815a39902133a10762"); + RemoveCreationDate(ps).ShouldMatchApproved(x => x.NoDiff()); } [Fact] @@ -67,9 +55,7 @@ public void can_render_postscript_qrcode_colors() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var ps = new PostscriptQRCode(data).GetGraphic(5, Color.Red, Color.Blue); - - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("2e001d7f67a446eb1b5df32ff5321808"); + RemoveCreationDate(ps).ShouldMatchApproved(x => x.NoDiff()); } private static string RemoveCreationDate(string text) @@ -81,4 +67,3 @@ private static string RemoveCreationDate(string text) return Regex.Replace(text, pattern, string.Empty, RegexOptions.Multiline); } } -#endif diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..3290fe698636719d4fb6a6ed5f35d54f33fb777e GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_hex.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_hex.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..3290fe698636719d4fb6a6ed5f35d54f33fb777e GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_without_quietzones.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_standard_graphic_without_quietzones.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..4746258e59ba890debd7d256295e0e50ac37246d GIT binary patch literal 1828 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 WNmqqp{eRjE)MbwTT!8@qvi}F#woC^A literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_standard_border.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_standard_border.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..87623f7272a3a4b7af7225c4d94962e98f7f0240 GIT binary patch literal 7683 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 A1ONa4 literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_transparent_border.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_logo_and_with_transparent_border.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..733d6f135d3ee389899ef149bd51b6349d5a5efa GIT binary patch literal 7710 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 TU$xg1V(lE|(ea)T0+9ND2}1cw literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_transparent_logo_graphic.approved.gif b/QRCoderTests/QRCodeRendererTests.can_create_qrcode_with_transparent_logo_graphic.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..733d6f135d3ee389899ef149bd51b6349d5a5efa GIT binary patch literal 7710 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 Xo=roJSoa*#-YS= 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/QRCodeRendererTests.cs b/QRCoderTests/QRCodeRendererTests.cs index a8db4e62..7803b3b8 100644 --- a/QRCoderTests/QRCodeRendererTests.cs +++ b/QRCoderTests/QRCodeRendererTests.cs @@ -5,10 +5,8 @@ using Shouldly; using Xunit; - namespace QRCoderTests; - public class QRCodeRendererTests { [Fact] @@ -17,9 +15,7 @@ public void can_create_qrcode_standard_graphic() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new QRCode(data).GetGraphic(10); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + bmp.ShouldMatchApproved(); } [Fact] @@ -28,9 +24,7 @@ public void can_create_qrcode_standard_graphic_hex() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new QRCode(data).GetGraphic(10, "#000000", "#ffffff"); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + bmp.ShouldMatchApproved(); } @@ -40,9 +34,7 @@ public void can_create_qrcode_standard_graphic_without_quietzones() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new QRCode(data).GetGraphic(5, Color.Black, Color.White, false); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c401d45c01e636af3eb4b8ca6cd17d14"); + bmp.ShouldMatchApproved(); } @@ -55,8 +47,7 @@ public void can_create_qrcode_with_transparent_logo_graphic() var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: HelperFunctions.GetIconBitmap()); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); + bmp.ShouldMatchApproved(); } [Fact] @@ -67,9 +58,7 @@ public void can_create_qrcode_with_non_transparent_logo_graphic() var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: HelperFunctions.GetIconBitmap()); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("74808e52270bba92e7b821dbd067dfd2"); + bmp.ShouldMatchApproved(); } [Fact] @@ -82,8 +71,7 @@ public void can_create_qrcode_with_logo_and_with_transparent_border() var logo = HelperFunctions.GetIconBitmap(); var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); + bmp.ShouldMatchApproved(); } [Fact] @@ -96,8 +84,7 @@ public void can_create_qrcode_with_logo_and_with_standard_border() var logo = HelperFunctions.GetIconBitmap(); var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: logo, iconBorderWidth: 6); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("943ecd2a847a4d9509ca0266dbbadd7b"); + bmp.ShouldMatchApproved(); } [Fact] @@ -110,8 +97,7 @@ public void can_create_qrcode_with_logo_and_with_custom_border() var logo = HelperFunctions.GetIconBitmap(); var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6, iconBackgroundColor: Color.DarkGreen); //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("e60bdaafe807889ca322d47146fe8300"); + bmp.ShouldMatchApproved(); } [Fact] @@ -127,9 +113,7 @@ public void can_render_qrcode_from_helper() { //Create QR code var bmp = QRCodeHelper.GetQRCode("This is a quick test! 123#?", 10, Color.Black, Color.White, QRCodeGenerator.ECCLevel.H); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + bmp.ShouldMatchApproved(); } } #endif diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj index 18a80723..b201ea79 100644 --- a/QRCoderTests/QRCoderTests.csproj +++ b/QRCoderTests/QRCoderTests.csproj @@ -1,4 +1,4 @@ - + net462;netcoreapp2.1;netcoreapp3.1;net5.0;net5.0-windows;net6.0;net6.0-windows true @@ -18,6 +18,8 @@ 2.1.30 + + diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode.approved.svg new file mode 100644 index 00000000..0325ba28 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode.approved.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_from_helper.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_from_helper.approved.svg new file mode 100644 index 00000000..12f0c5df --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_from_helper.approved.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_simple.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_simple.approved.svg new file mode 100644 index 00000000..f96419f8 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_simple.approved.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode.approved.svg new file mode 100644 index 00000000..cffad3c3 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode.approved.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode_viewboxattr.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode_viewboxattr.approved.svg new file mode 100644 index 00000000..9c396ee8 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_viewbox_mode_viewboxattr.approved.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg new file mode 100644 index 00000000..b47bc392 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg new file mode 100644 index 00000000..90c1fad7 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg new file mode 100644 index 00000000..aa8fb9a5 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bytearray.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bytearray.approved.svg new file mode 100644 index 00000000..7f72f4c2 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bytearray.approved.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_embedded.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_embedded.approved.svg new file mode 100644 index 00000000..e749b0be --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_embedded.approved.svg @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_image_tag.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_image_tag.approved.svg new file mode 100644 index 00000000..9b150b37 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_svg_logo_image_tag.approved.svg @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones.approved.svg new file mode 100644 index 00000000..4d3184a3 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones.approved.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones_hex.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones_hex.approved.svg new file mode 100644 index 00000000..7ea9ecf6 --- /dev/null +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_without_quietzones_hex.approved.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.cs b/QRCoderTests/SvgQRCodeRendererTests.cs index 7cc8ed08..7b26b13c 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.cs +++ b/QRCoderTests/SvgQRCodeRendererTests.cs @@ -18,9 +18,7 @@ public void can_render_svg_qrcode_simple() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); var svg = new SvgQRCode(data).GetGraphic(5); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("5c251275a435a9aed7e591eb9c2e9949"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -30,9 +28,7 @@ public void can_render_svg_qrcode() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("1baa8c6ac3bd8c1eabcd2c5422dd9f78"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -42,9 +38,7 @@ public void can_render_svg_qrcode_viewbox_mode() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var svg = new SvgQRCode(data).GetGraphic(new Size(128, 128)); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("56719c7db39937c74377855a5dc4af0a"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -54,9 +48,7 @@ public void can_render_svg_qrcode_viewbox_mode_viewboxattr() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var svg = new SvgQRCode(data).GetGraphic(new Size(128, 128), sizingMode: SvgQRCode.SizingMode.ViewBoxAttribute); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("788afdb693b0b71eed344e495c180b60"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -66,9 +58,7 @@ public void can_render_svg_qrcode_without_quietzones() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White, false); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("2a582427d86b51504c08ebcbcf0472bd"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -78,9 +68,7 @@ public void can_render_svg_qrcode_without_quietzones_hex() var gen = new QRCodeGenerator(); var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var svg = new SvgQRCode(data).GetGraphic(10, "#000000", "#ffffff", false); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("4ab0417cc6127e347ca1b2322c49ed7d"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } #if SYSTEM_DRAWING && !NET5_0_OR_GREATER // .NET 5+ does not encode PNG images in a deterministic way, so the hash may be different across different runs @@ -97,9 +85,7 @@ public void can_render_svg_qrcode_with_png_logo_bitmap() logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("04b12051632549cbb1879a0fe1353731"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -115,9 +101,7 @@ public void can_render_svg_qrcode_with_png_logo_bitmap_without_background() logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("b40c6997f78a2ef31e0a298c68bd31df"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -133,9 +117,7 @@ public void can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones() logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.Black, Color.White, drawQuietZones: false, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("42c43d33fc41bfff07b12f43b367808c"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } #endif @@ -152,9 +134,7 @@ public void can_render_svg_qrcode_with_png_logo_bytearray() logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("7d53f25af04e52b20550deb2e3589e96"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -170,9 +150,7 @@ public void can_render_svg_qrcode_with_svg_logo_embedded() logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.SVG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("855eb988d3af035abd273ed1629aa952"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -187,9 +165,7 @@ public void can_render_svg_qrcode_with_svg_logo_image_tag() var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20, iconEmbedded: false); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("bd442ea77d45a41a4f490b8d41591e04"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } [Fact] @@ -205,9 +181,7 @@ public void can_render_svg_qrcode_from_helper() { //Create QR code var svg = SvgQRCodeHelper.GetQRCode("A", 2, "#000000", "#ffffff", QRCodeGenerator.ECCLevel.Q); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("f5ec37aa9fb207e3701cc0d86c4a357d"); + svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } } #endif diff --git a/QRCoderTests/XamlQRCodeRendererTests.can_create_xaml_qrcode_standard_graphic.approved.gif b/QRCoderTests/XamlQRCodeRendererTests.can_create_xaml_qrcode_standard_graphic.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..3290fe698636719d4fb6a6ed5f35d54f33fb777e GIT binary patch literal 5332 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 literal 0 HcmV?d00001 diff --git a/QRCoderTests/XamlQRCodeRendererTests.cs b/QRCoderTests/XamlQRCodeRendererTests.cs index 7929c91b..1ea00eaa 100644 --- a/QRCoderTests/XamlQRCodeRendererTests.cs +++ b/QRCoderTests/XamlQRCodeRendererTests.cs @@ -17,8 +17,7 @@ public void can_create_xaml_qrcode_standard_graphic() var xCode = new XamlQRCode(data).GetGraphic(10); var bmp = HelperFunctions.BitmapSourceToBitmap(xCode); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + bmp.ShouldMatchApproved(); } From 4c5251d9105da4c15af94cf5cdd17107a1d58ae2 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:35:40 -0400 Subject: [PATCH 07/16] update workflows --- .github/workflows/wf-build-test.yml | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/workflows/wf-build-test.yml b/.github/workflows/wf-build-test.yml index 097795cd..63ec5a9d 100644 --- a/.github/workflows/wf-build-test.yml +++ b/.github/workflows/wf-build-test.yml @@ -27,13 +27,14 @@ jobs: run: dotnet restore - name: Build library - run: dotnet build -c Release --no-restore + run: dotnet build -c Debug --no-restore - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: Compiled project path: ${{ github.workspace }} + retention-days: 1 test: needs: build @@ -67,18 +68,11 @@ jobs: run: | $coverage = if ("${{ matrix.dotnet-framework.coverage }}" -eq "true") { "/p:CollectCoverage=true /p:CoverletOutputFormat=opencover" } else { "" } $noBuild = if ("${{ matrix.dotnet-framework.no-build }}" -eq "true") { "--no-build" } else { "" } - dotnet test -c Release -f ${{ matrix.dotnet-framework.framework }} --nologo $noBuild $coverage + dotnet test -c Debug -f ${{ matrix.dotnet-framework.framework }} --nologo $noBuild $coverage additional-tests: - needs: build runs-on: windows-latest steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: Compiled project - path: ${{ github.workspace }} - - name: Install additional .NET SDKs uses: actions/setup-dotnet@v4 with: @@ -91,13 +85,3 @@ jobs: - name: Run API approval tests working-directory: QRCoderApiTests run: dotnet test -c Release --nologo --no-build - - clean: - needs: [build, test, additional-tests] - if: always() - runs-on: windows-latest - steps: - - name: Delete artifacts - uses: GeekyEggo/delete-artifact@v5 - with: - name: Compiled project From 068a6b305af1dd92b31c306a4a37eef0a7db0577 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:36:39 -0400 Subject: [PATCH 08/16] update --- .github/workflows/wf-build-test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wf-build-test.yml b/.github/workflows/wf-build-test.yml index 63ec5a9d..6cfe0080 100644 --- a/.github/workflows/wf-build-test.yml +++ b/.github/workflows/wf-build-test.yml @@ -73,7 +73,10 @@ jobs: additional-tests: runs-on: windows-latest steps: - - name: Install additional .NET SDKs + - uses: actions/checkout@v4 + name: Checkout Code + + - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x From 4c50539963fe92dd62cdb47802dcd81fa522cd61 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:41:02 -0400 Subject: [PATCH 09/16] update --- QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs | 4 ++-- ..._generator_can_generate_payload_encoding_koi8.approved.txt | 2 +- ...nerator_can_generate_payload_encoding_win1251.approved.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs index 38d7937c..8eb31ffa 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs @@ -31,7 +31,7 @@ public void russiapayment_generator_can_generate_payload_encoding_win1251() var name = "ООО «Три кита»"; var correspAcc = "30101810965770000413"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.windows_1251); - generator.ToString().ShouldMatchApproved(x => x.NoDiff()); + generator.ToBytes().ShouldMatchApproved("txt"); } [Fact] @@ -43,7 +43,7 @@ public void russiapayment_generator_can_generate_payload_encoding_koi8() var name = "ООО «Три кита»"; var correspAcc = "30101810965770000413"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.koi8_r); - generator.ToString().ShouldMatchApproved(x => x.NoDiff()); + generator.ToBytes().ShouldMatchApproved("txt"); } [Fact] diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt index 32d355fa..ce2723c6 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_koi8.approved.txt @@ -1 +1 @@ -ST00013|Name=ООО <Три кита>|PersonalAcc=40702810138250123017|BankName=ОАО "БАНК"|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file +ST00013|Name= < >|PersonalAcc=40702810138250123017|BankName= ""|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt index 187e8650..1a05840b 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.russiapayment_generator_can_generate_payload_encoding_win1251.approved.txt @@ -1 +1 @@ -ST00011|Name=ООО «Три кита»|PersonalAcc=40702810138250123017|BankName=ОАО "БАНК"|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file +ST00011|Name= |PersonalAcc=40702810138250123017|BankName= ""|BIC=044525225|CorrespAcc=30101810965770000413| \ No newline at end of file From 17e778be4ecf233805828fae1102f2000cca9bef Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:44:51 -0400 Subject: [PATCH 10/16] update --- .github/workflows/wf-build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wf-build-test.yml b/.github/workflows/wf-build-test.yml index 6cfe0080..c1edb696 100644 --- a/.github/workflows/wf-build-test.yml +++ b/.github/workflows/wf-build-test.yml @@ -87,4 +87,4 @@ jobs: - name: Run API approval tests working-directory: QRCoderApiTests - run: dotnet test -c Release --nologo --no-build + run: dotnet test -c Release --nologo From b5d7205ab585ad7bdef02e70c11b7f03344bd49b Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:52:08 -0400 Subject: [PATCH 11/16] update --- QRCoderDemo/QRCoderDemo.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/QRCoderDemo/QRCoderDemo.csproj b/QRCoderDemo/QRCoderDemo.csproj index 496e9820..056a193b 100644 --- a/QRCoderDemo/QRCoderDemo.csproj +++ b/QRCoderDemo/QRCoderDemo.csproj @@ -1,10 +1,11 @@ - net45;net5.0-windows + net462;net5.0-windows true Exe false + true @@ -12,7 +13,7 @@ - + From c5347167b13947be2a51edc5e0c7e5292affb30d Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 13:59:10 -0400 Subject: [PATCH 12/16] update --- QRCoderDemo/QRCoderDemo.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/QRCoderDemo/QRCoderDemo.csproj b/QRCoderDemo/QRCoderDemo.csproj index 056a193b..9fb49e47 100644 --- a/QRCoderDemo/QRCoderDemo.csproj +++ b/QRCoderDemo/QRCoderDemo.csproj @@ -5,7 +5,7 @@ true Exe false - true + true @@ -16,6 +16,10 @@ + + + + {aa6be23a-7813-4d2a-835e-b673631ae9f1} From 33e64d791f2e712f79d7b03afc554d39beb0bba6 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 14:10:59 -0400 Subject: [PATCH 13/16] Refactor exception handling in tests to use Should.Throw for improved readability and consistency --- .../PayloadGeneratorTests/BezahlCodeTests.cs | 84 ++++--------- .../PayloadGeneratorTests/GirocodeTests.cs | 40 ++----- .../MoneroTransactionTests.cs | 8 +- .../RussiaPaymentOrderTests.cs | 19 +-- .../ShadowSocksConfigTests.cs | 8 +- .../PayloadGeneratorTests/SwissQrCodeTests.cs | 113 +++++------------- 6 files changed, 69 insertions(+), 203 deletions(-) diff --git a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs index bcebbc7b..e2587b25 100644 --- a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs @@ -329,10 +329,8 @@ public void bezahlcode_generator_should_throw_wrong_contact_constructor_exceptio var bnc = "10020 5000"; var name = "Wikimedia Fördergesellschaft"; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); } @@ -344,10 +342,8 @@ public void bezahlcode_generator_should_throw_wrong_contact_v2_constructor_excep var bic = "BFSWDE33BER"; var name = "Wikimedia Fördergesellschaft"; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban: iban, bic: bic)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban: iban, bic: bic)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); } @@ -360,10 +356,8 @@ public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_exceptio var name = "Wikimedia Fördergesellschaft"; var amount = 10; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, account: account, bnc: bnc, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, account: account, bnc: bnc, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); } @@ -382,10 +376,8 @@ public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_periodic var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); } @@ -398,10 +390,8 @@ public void bezahlcode_generator_should_throw_wrong_sepa_constructor_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); } @@ -421,10 +411,8 @@ public void bezahlcode_generator_should_throw_wrong_sepa_constructor_periodic_ex var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); } @@ -438,10 +426,8 @@ public void bezahlcode_generator_should_throw_name_too_long_exception() var name = "Wikimedia Fördergesellschaft has really really really long name, over 71 chars"; var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); } @@ -456,10 +442,8 @@ public void bezahlcode_generator_should_throw_reason_too_long_exception() var reason = "A long long long reason text which may resolve in an exception" + new string('.', 200); var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount, reason: reason)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount, reason: reason)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Reasons texts have to be shorter than 141 chars."); } @@ -473,10 +457,8 @@ public void bezahlcode_generator_should_throw_invalid_account_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The account entered isn't valid."); } @@ -489,10 +471,8 @@ public void bezahlcode_generator_should_throw_invalid_bnc_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The bnc entered isn't valid."); } @@ -506,10 +486,8 @@ public void bezahlcode_generator_should_throw_invalid_postingkey_exception() var postingKey = 101; var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("PostingKey must be within 0 and 99."); } @@ -522,10 +500,8 @@ public void bezahlcode_generator_should_throw_invalid_iban_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -539,10 +515,8 @@ public void bezahlcode_generator_should_throw_invalid_bic_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The BIC entered isn't valid."); } @@ -561,10 +535,8 @@ public void bezahlcode_generator_should_throw_separeference_too_long_exception() var amount = 10.00m; var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("SEPA reference texts have to be shorter than 36 chars."); } @@ -583,10 +555,8 @@ public void bezahlcode_generator_should_throw_invalid_creditorid_exception() var amount = 10.00m; var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The creditorId entered isn't valid."); } @@ -604,10 +574,8 @@ public void bezahlcode_generator_should_throw_invalid_mandateid_exception() var amount = 10.00m; var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The mandateId entered isn't valid."); } @@ -619,10 +587,8 @@ public void bezahlcode_generator_should_throw_amount_too_much_digits_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 10.001m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); } @@ -636,10 +602,8 @@ public void bezahlcode_generator_should_throw_amount_too_big_exception() var name = "Wikimedia Fördergesellschaft"; var amount = 1000000000m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -657,10 +621,8 @@ public void bezahlcode_generator_should_throw_invalid_executiondate_exception() var executionDate = new DateTime(2017, 1, 1); var currency = Currency.USD; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); #pragma warning restore CS0612 - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Execution date must be today or in future."); } @@ -680,10 +642,8 @@ public void bezahlcode_generator_should_throw_invalid_periodictimeunit_exception var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); } @@ -703,10 +663,8 @@ public void bezahlcode_generator_should_throw_invalid_periodictimeunitrotation_e var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); } } diff --git a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs index f7df3a60..57e0d469 100644 --- a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs @@ -136,11 +136,9 @@ public void girocode_generator_should_throw_iban_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -157,11 +155,9 @@ public void girocode_generator_should_throw_bic_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The BIC entered isn't valid."); } @@ -178,11 +174,9 @@ public void girocode_generator_should_throw_name_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); } @@ -199,11 +193,9 @@ public void girocode_generator_should_throw_amount_decimals_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); } @@ -219,11 +211,9 @@ public void girocode_generator_should_throw_amount_min_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -241,11 +231,9 @@ public void girocode_generator_should_throw_amount_max_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); } @@ -262,11 +250,9 @@ public void girocode_generator_should_throw_purpose_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Purpose of credit transfer can only have 4 chars at maximum."); } @@ -282,11 +268,9 @@ public void girocode_generator_should_throw_remittance_unstructured_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Unstructured reference texts have to be shorter than 141 chars."); } @@ -302,11 +286,9 @@ public void girocode_generator_should_throw_remittance_structured_exception() var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Structured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Structured reference texts have to be shorter than 36 chars."); } @@ -322,11 +304,9 @@ public void girocode_generator_should_throw_usermessage_exception() var messageToGirocodeUser = "The usermessage is shown to the user which scans the Girocode. It has to be shorter than 71 chars."; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Message to the Girocode-User reader texts have to be shorter than 71 chars."); } diff --git a/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs b/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs index d7077b00..fab131de 100644 --- a/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/MoneroTransactionTests.cs @@ -65,10 +65,8 @@ public void monero_generator_should_throw_wrong_amount_exception() var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; var amount = -1f; - var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address, amount)); + var exception = Should.Throw(() => new PayloadGenerator.MoneroTransaction(address, amount)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'txAmount' must be greater than 0."); } @@ -78,10 +76,8 @@ public void monero_generator_should_throw_no_address_exception() { var address = ""; - var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address)); + var exception = Should.Throw(() => new PayloadGenerator.MoneroTransaction(address)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The address is mandatory and has to be set."); } } diff --git a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs index 8eb31ffa..c05092c8 100644 --- a/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/RussiaPaymentOrderTests.cs @@ -71,9 +71,7 @@ public void russiapayment_generator_should_throw_no_separator_exception() var correspAcc = "30101810400000000225"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - var exception = Record.Exception(() => generator.ToString()); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); + var exception = Should.Throw(() => generator.ToString()); exception.Message.ShouldBe("No valid separator found."); } @@ -89,9 +87,7 @@ public void russiapayment_generator_should_throw_data_too_long_exception() var correspAcc = "30101810400000000225"; var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - var exception = Record.Exception(() => generator.ToString()); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); + var exception = Should.Throw(() => generator.ToString()); exception.Message.ShouldStartWith("Data too long"); } @@ -114,8 +110,7 @@ public void russiapayment_generator_should_throw_no_data_too_long_exception() // Should throw no exception as the 300 byte limit applies only to the mandatory fields // See https://github.com/Shane32/QRCoder/issues/392 - var exception = Record.Exception(() => generator.ToString()); - exception.ShouldBeNull(); + Should.NotThrow(() => generator.ToString()); } [Fact] @@ -127,9 +122,7 @@ public void russiapayment_generator_should_throw_must_not_be_null_exception() var name = "|@;:^_~{}!#$%&()*+,/"; var correspAcc = "30101810400000000225"; - var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); + var exception = Should.Throw(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); exception.Message.ShouldBe($"The input for 'PersonalAcc' must not be null."); } @@ -142,9 +135,7 @@ public void russiapayment_generator_should_throw_unmatched_pattern_exception() var name = "|@;:^_~{}!#$%&()*+,/"; var correspAcc = "30101810400000000225"; - var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); + var exception = Should.Throw(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); exception.Message.ShouldBe("The input for 'BIC' (abcd) doesn't match the pattern ^\\d{9}$"); } diff --git a/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs b/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs index fcdcf82e..62690b25 100644 --- a/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/ShadowSocksConfigTests.cs @@ -44,10 +44,8 @@ public void shadowsocks_generator_should_throw_portrange_low_exception() var password = "s3cr3t"; var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); + var exception = Should.Throw(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); } @@ -60,10 +58,8 @@ public void shadowsocks_generator_should_throw_portrange_high_exception() var password = "s3cr3t"; var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); + var exception = Should.Throw(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); } diff --git a/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs b/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs index 85b7d078..387ecd77 100644 --- a/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/SwissQrCodeTests.cs @@ -17,10 +17,8 @@ public void swissqrcode_generator_should_throw_reference_not_allowed() var reference = "1234567890123456"; var refTextType = ReferenceTextType.CreditorReferenceIso11649; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Reference is only allowed when referenceType not equals \"NON\""); } @@ -30,10 +28,8 @@ public void swissqrcode_generator_should_throw_missing_reftexttype() var refType = ReferenceType.SCOR; var reference = "1234567890123456"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("You have to set an ReferenceTextType when using the reference text."); } @@ -45,10 +41,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_too_long() var reference = "9900050000000003200710123031234654574398214093682164062138462089364"; var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-references have to be shorter than 28 chars."); } @@ -60,10 +54,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_wrong_char() var reference = "99000ABCDF5000032007101230"; var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-reference must exist out of digits only."); } @@ -75,10 +67,8 @@ public void swissqrcode_generator_should_throw_qrr_ref_checksum_invalid() var reference = "990005000000000320071012304"; var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("QR-references is invalid. Checksum error."); } @@ -90,10 +80,8 @@ public void swissqrcode_generator_should_throw_iso11649_ref_too_long() var reference = "99000500000000032007101230312346545743982162138462089364"; var refTextType = ReferenceTextType.CreditorReferenceIso11649; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Creditor references (ISO 11649) have to be shorter than 26 chars."); } @@ -103,10 +91,8 @@ public void swissqrcode_generator_should_throw_unstructured_msg_too_long() var billInformation = "This is sample bill information with a length below 140."; var unstructuredMessage = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.AdditionalInformation(unstructuredMessage, billInformation)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.AdditionalInformation(unstructuredMessage, billInformation)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Unstructured message and bill information must be shorter than 141 chars in total/combined."); } @@ -170,10 +156,8 @@ public void swissqrcode_generator_should_throw_invalid_iban() var iban = "CHC2609000000857666015"; var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN entered isn't valid."); } @@ -183,10 +167,8 @@ public void swissqrcode_generator_should_throw_invalid_qriban() var iban = "CHC2609000000857666015"; var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The QR-IBAN entered isn't valid."); } @@ -196,10 +178,8 @@ public void swissqrcode_generator_should_throw_ivalid_iban_country() var iban = "DE2609000000857666015"; var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("The IBAN must start with \"CH\" or \"LI\"."); } @@ -248,10 +228,8 @@ public void swissqrcode_generator_should_throw_name_empty() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Name must not be empty."); } @@ -266,10 +244,8 @@ public void swissqrcode_generator_should_throw_name_too_long() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Name must be shorter than 71 chars."); } @@ -284,10 +260,8 @@ public void swissqrcode_generator_should_throw_street_too_long() var street = "Parlamentsgebäude in der wunderschönen aber auch ziemlich teuren Stadt Bern in der Schweiz"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Street must be shorter than 71 chars."); } @@ -302,10 +276,8 @@ public void swissqrcode_generator_should_throw_street_with_illegal_char() var street = "Parlamentsgebäude 1 ♥"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe(@"Street must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); } @@ -320,10 +292,8 @@ public void swissqrcode_generator_should_throw_housenumber_too_long() var street = "Parlamentsgebäude"; var houseNumber = "123456789123456789"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("House number must be shorter than 17 chars."); } @@ -338,10 +308,8 @@ public void swissqrcode_generator_should_throw_zip_empty() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Zip code must not be empty."); } @@ -355,10 +323,8 @@ public void swissqrcode_generator_should_throw_zip_too_long() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Zip code must be shorter than 17 chars."); } @@ -373,10 +339,8 @@ public void swissqrcode_generator_should_throw_zip_has_illegal_char() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe(@"Zip code must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); } @@ -391,10 +355,8 @@ public void swissqrcode_generator_should_throw_city_empty() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("City must not be empty."); } @@ -409,10 +371,8 @@ public void swissqrcode_generator_should_throw_city_too_long() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("City name must be shorter than 36 chars."); } @@ -427,10 +387,8 @@ public void swissqrcode_generator_should_throw_wrong_countrycode() var street = "Parlamentsgebäude"; var houseNumber = "1"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); } @@ -550,10 +508,8 @@ public void swissqrcode_generator_should_throw_amount_too_big() var amount = 1234567891.25m; var reqDateOfPayment = new DateTime(2017, 03, 01); - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Amount (including decimals) must be shorter than 13 places."); } @@ -568,10 +524,8 @@ public void swissqrcode_generator_should_throw_incompatible_reftype() var amount = 100.25m; var reqDateOfPayment = new DateTime(2017, 03, 01); - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); } @@ -587,10 +541,8 @@ public void swissqrcode_generator_should_throw_alt1_too_long() var reqDateOfPayment = new DateTime(2017, 03, 01); var alt1 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Alternative procedure information block 1 must be shorter than 101 chars."); } @@ -606,10 +558,8 @@ public void swissqrcode_generator_should_throw_alt2_too_long() var reqDateOfPayment = new DateTime(2017, 03, 01); var alt1 = "lorem ipsum"; var alt2 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1, alt2)); + var exception = Should.Throw(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1, alt2)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Alternative procedure information block 2 must be shorter than 101 chars."); } @@ -622,27 +572,22 @@ public void swissqrcode_generator_should_validate_two_lettercodes() // Should work, as DE is a valid country code string country = "DE"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - exception.ShouldBeNull(); + Should.NotThrow(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); // Should work, as de is a valid country code and case should be ignored country = "de"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - exception.ShouldBeNull(); + Should.NotThrow(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); // Should work, as XK is is defined as special case (not officially ISO-3166-1,but used in the wild) // See https://en.wikipedia.org/wiki/XK_(user_assigned_code) and https://github.com/Shane32/QRCoder/issues/420 country = "XK"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - exception.ShouldBeNull(); + Should.NotThrow(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); // Should throw exception, as ZZ isn't a valid country code country = "ZZ"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); + var exception = Should.Throw(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - exception.ShouldNotBeNull(); - exception.ShouldBeOfType(); exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); } } From 77183bb17d3e2c9ddb3c9b19e9f71ee521318028 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 15:15:15 -0400 Subject: [PATCH 14/16] Enhance image approval methods to support monochrome conversion for improved accuracy in tests --- ...can_render_base64_qrcode_jpeg.approved.gif | Bin 2446 -> 1395 bytes QRCoderTests/Base64QRCodeRendererTests.cs | 2 +- .../ShouldMatchApprovedBinaryExtensions.cs | 8 +++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/QRCoderTests/Base64QRCodeRendererTests.can_render_base64_qrcode_jpeg.approved.gif b/QRCoderTests/Base64QRCodeRendererTests.can_render_base64_qrcode_jpeg.approved.gif index 5dbb6746d8e9c0e02945418ebc5809e010f5e915..ea6ee4617ec964db2ed52da614f099a1e6f19b05 100644 GIT binary patch delta 1330 zcmV-21EN9GtOVYtY+ zM~T-M_^25fF?s2xxT#?%m)B8xky^4^(R#RuDwy#qQY$%Yipg>7nQPKp_{&+j5&Up0 zJ4!n|7|YvCkqo_mdto#vp8TfIvx+UXp6t-IF1?dwerZn^$eo{hFW-z^TyK7N1l znU8;OjX^qL@6=>U(GOlbVgnU4q*t)vG-I;@>chscUdBZK@iyvNh_K?fi{?IFq;&9~ z!Hyv#Y9uM|oX27?XC}nh65vRcCAajn$TOzNpek-k6zLCtRKcG}_W^6V5+u{!IaqEPeXO&(B zb!g<_fov~7ev5h@4hm}#?`-LCwqUV>Dlw;kNbGY(D$Eg2nsk9 zQ}vzIhh1-3MqPshdIup*Y}NM}cY^6hnlgVSl^2J9AttsT2!pNYU4fff_yUW24K^Wm znNjG1j3e&IV2_6l7-NaK*_U925Qb&$Xg7Lc9|-V zuxVPVqabps;;lo18Xc=e=_)C&?hIMxXe2gS=&+Li7CWnsNecVxv2>OOER<k!QA=tW>9A z3#zE%(i`WWjDkxlnFoIe8$5Kk*&tUjW;sm5$>{B5)rYv-}Hk(!t3 zoe6`y@_Q}!nX$(qmu#!UidKBdz;({Nv!5`lx-S&IN-S`I&_;1I!k%TiUehFZ8!*#< zF(-}nxJw&-HPT*hcrwn}q0Dgz=Q-Oe%`KcADYGc5{V8u~E9-LH{z6SR+zj8{FK`{> zi_Df|id^*H><%urn3U#Ow!C61JnqxTE>3vk`TtrDww&Ce8ZP6HAMQBdXxd9|)?@2D zFXRZT9C7A$*F36`of8V$=b>|svFo{ivwo=VCfi)A?_cv?y4HKc9(=oR!>M|Puqtm; z@XjjEZ`f?3{xRjiPhS1@^BOFu(9v^$y7iS}@AJ#!@9LWO*?ZV^=HRnW@%ryO9nJM& zr|&%OvMUY1|A}vS?R%cw@`pfu?QeGl^dEPccR&Tws&Hll+V>Jzx%<6}f%Ut8oclJo zFzvbRb|gd|(RxR+YxxayCgjo75H~^(8qR(oG@b}8)x8Uvy>z>OrLq#S!8t`{l#Nq0y*2LDuXN+VlqZ!YL#x$yN ojcjbA8{Y`WILdL3bgZKt?}*0{JnC_eeC(qi{|LxHil6`hJ61Nb-T(jq delta 2389 zcmV-b399z<3XT&QM@dFFIbo#$r2z2&00000_V)G&2nhcE{*ei|>11MA1qVS=SfnR%v(u_(wYW$(nGP!5Ukd5Ne94nksrw8_USjtJioS z1rRzLXuRNsddphJ8~2NwNPotxP>enKRvU$ZiiT!=BHn-%ptV@e3mx5kSiZ_O5qYeF z_nH+-*#P%ExPJkQzIWbmrTBJ+A1N+W*!k0DOIZ|p#ER|7a88mz6#wKba8oEiAEAq3 z;Aw1wuYo^leXtZV=8{gSZ5xLTs5PgKJ7ohAos^TU-ocHWg#Lu6%75IcU>z4&D;h82 zIHH@3LS;Z`!qcizS;~u=wJX>`5(7M}sesB>VPfr3WxG^YGhh7Hk%X|8ZrrbKY zH{#W~zzj8ftCz4>!CV8+Bs&&owoZ=6QdaBC~ZTbJVOl?|){?NXf?ZJ9u?w;GKn6X7Jgs&D^+w8l8#~L2RY~&}p_A1#~Dc zYHJ&(O_Q;56Zc5_K5n}buXmcf>kfbS`)%UFexsJ3v7)1Dy*UH_4fY&)3z;X!I|xmd z#Z7`x!jOLLp!b)1XqCW>6e-PSp-;nrHepE!wj}`=O^q;OS$}18=NCQkv%l0xHA*kO$jIsjNox^+-u2;y8QpF|Pfh}|`^6?s9BAOMnKlK@JvpdCb(ryof~ zR;J!EdS2ZD+XIOOJy%SUUx`u z5k+dNcdUJO44fix2$xgA#-*iets%M!G}DHAQhd;zihmWg1Quh)dpZ82Ym$x1qLRGe z-A7AvNFMoMdL}ruTfBN;+b10B@k%`h<1d~v5E zGQy5l64IHYv5Pfg>dH295?_=VZ>uy9ewsNMi+qml%AJ7!KMoEe9Om?xngGHBv zvbKSIs$I+>W=D(^&I{@bgcOCc>FM2c^G)aChkvhqyHq;Noo!1B2Xiz~dkEAYv7hG7 zYs;8C12W)+U&iJj^%CV!58R2l%aSvl^&jzH9oC76R6_%64rKIn9I&H@Z?Ev;(nr(C z8%>8c_KlcxE5RIZ7&uu8NnpWS$5x8>jzT@)b^y^@fS`6U{{5|YwgMjeEBZti#1~U@?Z)-m>oa&SpvdXE-e<>SrK2Qio{$;{c4b)0(HDk%g|{gmtb6lz;1`ZHsvO*!)TuoQ}2WB4^YHthA;D1!m9# zFtX5xfR;A5`ED=%a3U155Q&f2Y#)wX(y#&ru-(PNT~>q*pdw&Cxm3@FbNN~fRvF6S zA#M`&iVX$$L%;u3t_3TJmfm7%0Q@Zyk}S)RHTbx|X~3{6LoDOt$n?B{RmoUQk$)L5 z_Z7ULRdHjT%Hbay7X?NBkOc8doeC9|%}ILZUS6aTGP|}MTS|viuoTn-wKfT&t&5a6 z62&I>luI{p@R}QBWjC_u&tay)TmyBco0gM20vXW%Ii*x5$dsrgJ)q5kz|dXNI{HIo zxxgC5Q^r5`AAY)Xw1-1Ke@`|QBp&>vEbkw zS|xFURz5mTlQ_B>Z)PhEBU%tAA4lB%dJZheE+tM&E+2p4$YhcQ;VoH@!EI62U8aPt!2w z42-t^x@bzn=gPAJIELv=2z(pJq*TIjYG--HXz{B?&L;D&9V#ujFx4i%4Vb0r6fkN; zs=y)15{nMyt-<9g4uX#}oY5eMFOwN}gc^R2Jh3V=E?*Xe0hY0U6643Cq!G2@-7#fl zNmaGYRF0OCq6b%FAAbgg8O(e(P#1Hlj>Nv`v5mHCj5X>ruO@n&>*#W^rR-A815tX-Ex&hn858EH|BKz{j#Swjes!>!hilmJ zw~uS^-jy=^Wl@3C)?D2tX35M`($#vg*zUGSCeXH`O|`&y+0Tylw5xsXY;U{U-wyY<%YE*2ue;q% HPyhfs@kot3 diff --git a/QRCoderTests/Base64QRCodeRendererTests.cs b/QRCoderTests/Base64QRCodeRendererTests.cs index 8826d150..74d83718 100644 --- a/QRCoderTests/Base64QRCodeRendererTests.cs +++ b/QRCoderTests/Base64QRCodeRendererTests.cs @@ -54,7 +54,7 @@ public void can_render_base64_qrcode_jpeg() { var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Black, Color.White, true, Base64QRCode.ImageType.Jpeg); var data = Convert.FromBase64String(base64QRCode); - data.ShouldMatchApprovedImage(); + data.ShouldMatchApprovedImage(asMonochrome: true); // remove JPEG compression artifacts by converting to monochrome } #endif } diff --git a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs index 4179843b..cb99e14d 100644 --- a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs +++ b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs @@ -17,16 +17,18 @@ namespace Shouldly; [EditorBrowsable(EditorBrowsableState.Never)] public static partial class ShouldMatchApprovedExtensions { - public static void ShouldMatchApprovedImage(this byte[] imageBytes, string? discriminator = null, string? customMessage = null) + public static void ShouldMatchApprovedImage(this byte[] imageBytes, string? discriminator = null, string? customMessage = null, bool asMonochrome = false) { using var ms = new MemoryStream(imageBytes); using var image = (Bitmap)Image.FromStream(ms); - image.ShouldMatchApproved(discriminator, customMessage); + image.ShouldMatchApproved(discriminator, customMessage, asMonochrome); } - public static void ShouldMatchApproved(this Bitmap image, string? discriminator = null, string? customMessage = null) + public static void ShouldMatchApproved(this Bitmap image, string? discriminator = null, string? customMessage = null, bool asMonochrome = false) { // encode to gif first for easier visual verification, and using a third party lib to avoid platform-specific compression differences var readableBitmapData = image.GetReadableBitmapData(); + if (asMonochrome) + readableBitmapData = readableBitmapData.Clone(KnownPixelFormat.Format1bppIndexed); var ms = new MemoryStream(); GifEncoder.EncodeImage(readableBitmapData, ms); ms.ToArray().ShouldMatchApproved("gif", discriminator, customMessage); From 939e53101026f747298f5079ef7767aa62b1a32f Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 15:34:33 -0400 Subject: [PATCH 15/16] Update --- ...g_qrcode_with_png_logo_bitmap.approved.svg | 2 +- ..._png_logo_bitmap.embeddedLogo.approved.gif | Bin 0 -> 1410 bytes ...ogo_bitmap_without_background.approved.svg | 2 +- ...thout_background.embeddedLogo.approved.gif | Bin 0 -> 1410 bytes ...ogo_bitmap_without_quietzones.approved.svg | 2 +- ...thout_quietzones.embeddedLogo.approved.gif | Bin 0 -> 1410 bytes QRCoderTests/SvgQRCodeRendererTests.cs | 50 +++++++++++++++--- 7 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.embeddedLogo.approved.gif create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.embeddedLogo.approved.gif create mode 100644 QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.embeddedLogo.approved.gif diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg index b47bc392..fdf9f61f 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.approved.svg @@ -213,6 +213,6 @@ - + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.embeddedLogo.approved.gif b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap.embeddedLogo.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..7b265b04d160f5eeaa3042f5c22c42a592460d66 GIT binary patch literal 1410 zcmV-|1%3KQNk%w1VPpVg0Q4IGgEKGnuB-du-|%~P%uq}C!odFZ^zx63?Pp@IL_q6Y zR{6}z|NsA%J2&*Cpz(%*_qet0acuqT>bXcn+E!2c*46WwmcmR&pFTQ^H#7U;-~a#s z000000000000000A^sFgM|mJqPexQAM@dE?MQ&qnWMy&yA^8LW000{REC2ui0Av7U z000I5;3ke_X`X1Ru59bRa4gSs%>vj&P)e^*gb4;*^IQow0eQ-RrKDdnB*F@RZw3eGgld={3g zT&8rzE-SlN=&}WRn+;$Ktz@4RRS+k(t1NHhi=7=ftD6?3A1x>BK&z{P0l~Y=S~hzC zn?YU0&K3|tp&FWl1NR(QcxJ)ER0awLGtlssiG!&IcAL;ys9WfA)v zGt=8TwgX0WgK6dB=v4Wr=yf(R(* z6Be}@5S|?+0AR}%qTTnA6&c>w2N5H9bs-ax-Nzk?14?#BTq3A5Vh@x7WIzEYMgV~U z5NxEP9k^_u(2Gkb2cQBG6mX9K6&NsML{f3oM@S-217wjEQ~<$*d_C~MiT*%PWdIw( zG0O%(rs2RaVJ@OynP9}|OmmW~$)*z;2}xK11==(MBLi^R#{+{ckYY0uq$NRscaR_@ z9L5>2l^_2pQ$Pd=JaA?&2T&TRq<)O~V;>z2pg{p?rg6YC0u=Czm(bvM2b3GQXa{fz zAW%V2aDc|2T6gp)q8|_-RRFGNH2969cFtx8iyK&2$OF-dMeG+2m6Ou{rp^@h28eqW zqR|osX!}KN*BIacPtcV5#e0RQxmIQ7MMyN(72yM#a>uMZJ8iUqdcw1&;2XqbT0 zpZzQmfd&hx+n04tbqEboXedPjb&whW=K;4$AOMz*H8m(<5$I@*xL}Oet+)P0R#n0# zjf_>jV7zGi`awc6{<>X4#5GG-2}5*PhvM1 zH})AH-+qI#!ohqRaJSb5Xb>K`-XI=^#b;|WcoPm)u%WSSSq?_$LY{2y!vGR=-2=T^ zEzvg^ur5{lj3o^afVobvew6Ep_W*;ej1+l~(jfbwJ2v9Jjzy0mv3dM8$@eY;H$Xmr zGVfT36T_mdSPxC~SByDM_14w5y5BZnY>W5joNtB7G5~*y-sTbP1M@U+Z%PDj_wWDz zZ}Za+rZA^CZJ~gAVg&*d#fAmmOAih#7$Wo+LGkH9eh$Q6Ak-H@_*sI2FS4K|MnJs+ zYHx%nz+fvhh{6+mDi*P8AqyBc3e3q626WQ|;BGjBqcI|DJd{Eb`T&I>A_50GfCUhQ Qz%3&>@rh83Vh8{LJI?uVhyVZp literal 0 HcmV?d00001 diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg index 90c1fad7..b4366c61 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.approved.svg @@ -225,6 +225,6 @@ - + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.embeddedLogo.approved.gif b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_background.embeddedLogo.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..7b265b04d160f5eeaa3042f5c22c42a592460d66 GIT binary patch literal 1410 zcmV-|1%3KQNk%w1VPpVg0Q4IGgEKGnuB-du-|%~P%uq}C!odFZ^zx63?Pp@IL_q6Y zR{6}z|NsA%J2&*Cpz(%*_qet0acuqT>bXcn+E!2c*46WwmcmR&pFTQ^H#7U;-~a#s z000000000000000A^sFgM|mJqPexQAM@dE?MQ&qnWMy&yA^8LW000{REC2ui0Av7U z000I5;3ke_X`X1Ru59bRa4gSs%>vj&P)e^*gb4;*^IQow0eQ-RrKDdnB*F@RZw3eGgld={3g zT&8rzE-SlN=&}WRn+;$Ktz@4RRS+k(t1NHhi=7=ftD6?3A1x>BK&z{P0l~Y=S~hzC zn?YU0&K3|tp&FWl1NR(QcxJ)ER0awLGtlssiG!&IcAL;ys9WfA)v zGt=8TwgX0WgK6dB=v4Wr=yf(R(* z6Be}@5S|?+0AR}%qTTnA6&c>w2N5H9bs-ax-Nzk?14?#BTq3A5Vh@x7WIzEYMgV~U z5NxEP9k^_u(2Gkb2cQBG6mX9K6&NsML{f3oM@S-217wjEQ~<$*d_C~MiT*%PWdIw( zG0O%(rs2RaVJ@OynP9}|OmmW~$)*z;2}xK11==(MBLi^R#{+{ckYY0uq$NRscaR_@ z9L5>2l^_2pQ$Pd=JaA?&2T&TRq<)O~V;>z2pg{p?rg6YC0u=Czm(bvM2b3GQXa{fz zAW%V2aDc|2T6gp)q8|_-RRFGNH2969cFtx8iyK&2$OF-dMeG+2m6Ou{rp^@h28eqW zqR|osX!}KN*BIacPtcV5#e0RQxmIQ7MMyN(72yM#a>uMZJ8iUqdcw1&;2XqbT0 zpZzQmfd&hx+n04tbqEboXedPjb&whW=K;4$AOMz*H8m(<5$I@*xL}Oet+)P0R#n0# zjf_>jV7zGi`awc6{<>X4#5GG-2}5*PhvM1 zH})AH-+qI#!ohqRaJSb5Xb>K`-XI=^#b;|WcoPm)u%WSSSq?_$LY{2y!vGR=-2=T^ zEzvg^ur5{lj3o^afVobvew6Ep_W*;ej1+l~(jfbwJ2v9Jjzy0mv3dM8$@eY;H$Xmr zGVfT36T_mdSPxC~SByDM_14w5y5BZnY>W5joNtB7G5~*y-sTbP1M@U+Z%PDj_wWDz zZ}Za+rZA^CZJ~gAVg&*d#fAmmOAih#7$Wo+LGkH9eh$Q6Ak-H@_*sI2FS4K|MnJs+ zYHx%nz+fvhh{6+mDi*P8AqyBc3e3q626WQ|;BGjBqcI|DJd{Eb`T&I>A_50GfCUhQ Qz%3&>@rh83Vh8{LJI?uVhyVZp literal 0 HcmV?d00001 diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg index aa8fb9a5..a1d8cb88 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg +++ b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.approved.svg @@ -218,6 +218,6 @@ - + \ No newline at end of file diff --git a/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.embeddedLogo.approved.gif b/QRCoderTests/SvgQRCodeRendererTests.can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones.embeddedLogo.approved.gif new file mode 100644 index 0000000000000000000000000000000000000000..7b265b04d160f5eeaa3042f5c22c42a592460d66 GIT binary patch literal 1410 zcmV-|1%3KQNk%w1VPpVg0Q4IGgEKGnuB-du-|%~P%uq}C!odFZ^zx63?Pp@IL_q6Y zR{6}z|NsA%J2&*Cpz(%*_qet0acuqT>bXcn+E!2c*46WwmcmR&pFTQ^H#7U;-~a#s z000000000000000A^sFgM|mJqPexQAM@dE?MQ&qnWMy&yA^8LW000{REC2ui0Av7U z000I5;3ke_X`X1Ru59bRa4gSs%>vj&P)e^*gb4;*^IQow0eQ-RrKDdnB*F@RZw3eGgld={3g zT&8rzE-SlN=&}WRn+;$Ktz@4RRS+k(t1NHhi=7=ftD6?3A1x>BK&z{P0l~Y=S~hzC zn?YU0&K3|tp&FWl1NR(QcxJ)ER0awLGtlssiG!&IcAL;ys9WfA)v zGt=8TwgX0WgK6dB=v4Wr=yf(R(* z6Be}@5S|?+0AR}%qTTnA6&c>w2N5H9bs-ax-Nzk?14?#BTq3A5Vh@x7WIzEYMgV~U z5NxEP9k^_u(2Gkb2cQBG6mX9K6&NsML{f3oM@S-217wjEQ~<$*d_C~MiT*%PWdIw( zG0O%(rs2RaVJ@OynP9}|OmmW~$)*z;2}xK11==(MBLi^R#{+{ckYY0uq$NRscaR_@ z9L5>2l^_2pQ$Pd=JaA?&2T&TRq<)O~V;>z2pg{p?rg6YC0u=Czm(bvM2b3GQXa{fz zAW%V2aDc|2T6gp)q8|_-RRFGNH2969cFtx8iyK&2$OF-dMeG+2m6Ou{rp^@h28eqW zqR|osX!}KN*BIacPtcV5#e0RQxmIQ7MMyN(72yM#a>uMZJ8iUqdcw1&;2XqbT0 zpZzQmfd&hx+n04tbqEboXedPjb&whW=K;4$AOMz*H8m(<5$I@*xL}Oet+)P0R#n0# zjf_>jV7zGi`awc6{<>X4#5GG-2}5*PhvM1 zH})AH-+qI#!ohqRaJSb5Xb>K`-XI=^#b;|WcoPm)u%WSSSq?_$LY{2y!vGR=-2=T^ zEzvg^ur5{lj3o^afVobvew6Ep_W*;ej1+l~(jfbwJ2v9Jjzy0mv3dM8$@eY;H$Xmr zGVfT36T_mdSPxC~SByDM_14w5y5BZnY>W5joNtB7G5~*y-sTbP1M@U+Z%PDj_wWDz zZ}Za+rZA^CZJ~gAVg&*d#fAmmOAih#7$Wo+LGkH9eh$Q6Ak-H@_*sI2FS4K|MnJs+ zYHx%nz+fvhh{6+mDi*P8AqyBc3e3q626WQ|;BGjBqcI|DJd{Eb`T&I>A_50GfCUhQ Qz%3&>@rh83Vh8{LJI?uVhyVZp literal 0 HcmV?d00001 diff --git a/QRCoderTests/SvgQRCodeRendererTests.cs b/QRCoderTests/SvgQRCodeRendererTests.cs index 7b26b13c..b1642a1b 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.cs +++ b/QRCoderTests/SvgQRCodeRendererTests.cs @@ -1,7 +1,6 @@ -#if !NETCOREAPP1_1 using System; using System.Drawing; -using System.IO; +using System.Text.RegularExpressions; using QRCoder; using QRCoderTests.Helpers; using Shouldly; @@ -82,9 +81,21 @@ public void can_render_svg_qrcode_with_png_logo_bitmap() //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 var logoBitmap = HelperFunctions.GetIconBitmap(); var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + // remove PNG encoded bitmap from SVG and verify it separately (to avoid diffs due to different encoding settings of System.Drawing) + var regex = new Regex( + @"]*\bxlink:href\s*=\s*[""']data:[^;""']+;base64,([A-Za-z0-9+/=]+)[""']", + RegexOptions.IgnoreCase); + var match = regex.Match(svg); + if (!match.Success || match.Groups.Count < 2) + throw new Exception("Could not find embedded image data in SVG output."); + var base64Data = match.Groups[1].Value; + var imageData = Convert.FromBase64String(base64Data); + imageData.ShouldMatchApprovedImage(discriminator: "embeddedLogo"); + svg = svg.Replace(base64Data, "=====BASE64DATA====="); svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } @@ -98,9 +109,21 @@ public void can_render_svg_qrcode_with_png_logo_bitmap_without_background() //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 var logoBitmap = HelperFunctions.GetIconBitmap(); var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15, false); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + // remove PNG encoded bitmap from SVG and verify it separately (to avoid diffs due to different encoding settings of System.Drawing) + var regex = new Regex( + @"]*\bxlink:href\s*=\s*[""']data:[^;""']+;base64,([A-Za-z0-9+/=]+)[""']", + RegexOptions.IgnoreCase); + var match = regex.Match(svg); + if (!match.Success || match.Groups.Count < 2) + throw new Exception("Could not find embedded image data in SVG output."); + var base64Data = match.Groups[1].Value; + var imageData = Convert.FromBase64String(base64Data); + imageData.ShouldMatchApprovedImage(discriminator: "embeddedLogo"); + svg = svg.Replace(base64Data, "=====BASE64DATA====="); svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } @@ -114,9 +137,21 @@ public void can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones() //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 var logoBitmap = HelperFunctions.GetIconBitmap(); var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.Black, Color.White, drawQuietZones: false, logo: logoObj); + + // remove PNG encoded bitmap from SVG and verify it separately (to avoid diffs due to different encoding settings of System.Drawing) + var regex = new Regex( + @"]*\bxlink:href\s*=\s*[""']data:[^;""']+;base64,([A-Za-z0-9+/=]+)[""']", + RegexOptions.IgnoreCase); + var match = regex.Match(svg); + if (!match.Success || match.Groups.Count < 2) + throw new Exception("Could not find embedded image data in SVG output."); + var base64Data = match.Groups[1].Value; + var imageData = Convert.FromBase64String(base64Data); + imageData.ShouldMatchApprovedImage(discriminator: "embeddedLogo"); + svg = svg.Replace(base64Data, "=====BASE64DATA====="); svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } #endif @@ -131,7 +166,7 @@ public void can_render_svg_qrcode_with_png_logo_bytearray() //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 var logoBitmap = HelperFunctions.GetIconBytes(); var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); @@ -147,7 +182,7 @@ public void can_render_svg_qrcode_with_svg_logo_embedded() //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361 var logoSvg = HelperFunctions.GetIconSvg(); var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.SVG); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.SVG); var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); @@ -184,4 +219,3 @@ public void can_render_svg_qrcode_from_helper() svg.ShouldMatchApproved(x => x.NoDiff().WithFileExtension("svg")); } } -#endif From 4b3a626600c76d5fdd530c2353f02c0d7656f4c8 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 Oct 2025 15:36:26 -0400 Subject: [PATCH 16/16] Update using --- QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs index cb99e14d..bc9cc274 100644 --- a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs +++ b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs @@ -29,7 +29,7 @@ public static void ShouldMatchApproved(this Bitmap image, string? discriminator var readableBitmapData = image.GetReadableBitmapData(); if (asMonochrome) readableBitmapData = readableBitmapData.Clone(KnownPixelFormat.Format1bppIndexed); - var ms = new MemoryStream(); + using var ms = new MemoryStream(); GifEncoder.EncodeImage(readableBitmapData, ms); ms.ToArray().ShouldMatchApproved("gif", discriminator, customMessage); }