Skip to content

Commit 4ca3ec9

Browse files
committed
Add tests
1 parent b8ba8c9 commit 4ca3ec9

File tree

4 files changed

+113
-10
lines changed

4 files changed

+113
-10
lines changed

QRCoder/PayloadGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static class PayloadGenerator
1515
public abstract class Payload
1616
{
1717
public virtual int Version { get { return -1; } }
18-
public virtual QRCodeGenerator.ECCLevel? EccLevel { get { return null; } } // null is default
18+
public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.Default; } } // null is default
1919
public virtual QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Default; } }
2020
public abstract override string ToString();
2121
}
@@ -2380,7 +2380,7 @@ public class SlovenianUpnQr : Payload
23802380
private string _recipientSiReference = "";
23812381

23822382
public override int Version { get { return 15; } }
2383-
public override QRCodeGenerator.ECCLevel? EccLevel { get { return QRCodeGenerator.ECCLevel.M; } }
2383+
public override QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } }
23842384
public override QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Iso8859_2; } }
23852385

23862386
private string LimitLength(string value, int maxLength)

QRCoder/QRCodeGenerator.ECCLevel.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,35 @@ public partial class QRCodeGenerator
88
/// </summary>
99
public enum ECCLevel
1010
{
11+
/// <summary>
12+
/// Default error correction level, which will select Level M (Medium) unless otherwise specified by the payload.
13+
/// Level M allows approximately 15% of data to be recovered, offering a balance between data capacity and error recovery.
14+
/// </summary>
15+
Default = -1,
16+
1117
/// <summary>
1218
/// Level L: Low error correction (approximately 7% of data can be recovered).
1319
/// This level allows the highest data density.
1420
/// </summary>
15-
L,
21+
L = 0,
1622

1723
/// <summary>
1824
/// Level M: Medium error correction (approximately 15% of data can be recovered).
1925
/// Offers a balance between data capacity and error recovery.
2026
/// </summary>
21-
M,
27+
M = 1,
2228

2329
/// <summary>
2430
/// Level Q: Quartile error correction (approximately 25% of data can be recovered).
2531
/// More robust error correction at the cost of reduced data capacity.
2632
/// </summary>
27-
Q,
33+
Q = 2,
2834

2935
/// <summary>
3036
/// Level H: High error correction (approximately 30% of data can be recovered).
3137
/// Provides the highest level of error recovery, ideal for environments with high risk of data loss.
3238
/// </summary>
33-
H
39+
H = 3
3440
}
3541
}
3642
}

QRCoder/QRCodeGenerator.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public QRCodeData CreateQrCode(byte[] binaryData, ECCLevel eccLevel)
9393
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
9494
public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload)
9595
{
96-
return GenerateQrCode(payload.ToString(), payload.EccLevel ?? ECCLevel.M, false, false, payload.EciMode, payload.Version);
96+
return GenerateQrCode(payload.ToString(), payload.EccLevel, false, false, payload.EciMode, payload.Version);
9797
}
9898

9999
/// <summary>
@@ -105,8 +105,10 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload)
105105
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
106106
public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel)
107107
{
108-
if (payload.EccLevel.HasValue && eccLevel != payload.EccLevel.Value)
109-
throw new ArgumentException($"The provided payload requires a ECC level of {eccLevel}.", nameof(eccLevel));
108+
if (eccLevel == ECCLevel.Default)
109+
eccLevel = payload.EccLevel;
110+
else if (payload.EccLevel != ECCLevel.Default && eccLevel != payload.EccLevel)
111+
throw new ArgumentOutOfRangeException(nameof(eccLevel), $"The provided payload requires a ECC level of {eccLevel}.");
110112
return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version);
111113
}
112114

@@ -123,6 +125,7 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLev
123125
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
124126
public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1)
125127
{
128+
eccLevel = ValidateECCLevel(eccLevel);
126129
EncodingMode encoding = GetEncodingFromPlaintext(plainText, forceUtf8);
127130
var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8);
128131
var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8);
@@ -167,7 +170,6 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
167170
return GenerateQrCode(completeBitArray, eccLevel, version);
168171
}
169172

170-
171173
/// <summary>
172174
/// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation.
173175
/// </summary>
@@ -177,6 +179,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
177179
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
178180
public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
179181
{
182+
eccLevel = ValidateECCLevel(eccLevel);
180183
int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel);
181184

182185
int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte);
@@ -189,6 +192,27 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
189192
return GenerateQrCode(bitArray, eccLevel, version);
190193
}
191194

195+
/// <summary>
196+
/// Validates the specified error correction level.
197+
/// Returns the provided level if it is valid, or the level M if the provided level is Default.
198+
/// Throws an exception if an invalid level is provided.
199+
/// </summary>
200+
private static ECCLevel ValidateECCLevel(ECCLevel eccLevel)
201+
{
202+
switch (eccLevel)
203+
{
204+
case ECCLevel.L:
205+
case ECCLevel.M:
206+
case ECCLevel.Q:
207+
case ECCLevel.H:
208+
return eccLevel;
209+
case ECCLevel.Default:
210+
return ECCLevel.M;
211+
default:
212+
throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Invalid error correction level.");
213+
}
214+
}
215+
192216
private static readonly BitArray _repeatingPattern = new BitArray(
193217
new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true });
194218

QRCoderTests/QRGeneratorTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Collections;
99
using System.Text;
10+
using System;
1011

1112
namespace QRCoderTests
1213
{
@@ -226,6 +227,78 @@ bool IsValidISO(string input)
226227
}
227228
}
228229
}
230+
231+
[Fact]
232+
[Category("QRGenerator/EccLevel")]
233+
public void ecc_level_from_payload_works()
234+
{
235+
var stringValue = "this is a test";
236+
237+
// set up baselines
238+
var expectedL = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.L));
239+
var expectedM = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.M));
240+
var expectedQ = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.Q));
241+
var expectedH = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.H));
242+
243+
// ensure that the baselines are different from each other
244+
expectedL.ShouldNotBe(expectedM);
245+
expectedL.ShouldNotBe(expectedQ);
246+
expectedL.ShouldNotBe(expectedH);
247+
expectedM.ShouldNotBe(expectedQ);
248+
expectedM.ShouldNotBe(expectedH);
249+
expectedQ.ShouldNotBe(expectedH);
250+
251+
// validate that any ECC level can be used when the payload specifies a default ECC level
252+
var payloadDefault = new SamplePayload(stringValue, QRCodeGenerator.ECCLevel.Default);
253+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault)).ShouldBe(expectedM);
254+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Default)).ShouldBe(expectedM);
255+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.L)).ShouldBe(expectedL);
256+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.M)).ShouldBe(expectedM);
257+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Q)).ShouldBe(expectedQ);
258+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.H)).ShouldBe(expectedH);
259+
260+
// validate that the ECC level specified in the payload is used when default is specified,
261+
// or checks that the selected ECC level matches the payload ECC level, throwing an exception otherwise
262+
Verify(QRCodeGenerator.ECCLevel.L, expectedL);
263+
Verify(QRCodeGenerator.ECCLevel.M, expectedM);
264+
Verify(QRCodeGenerator.ECCLevel.Q, expectedQ);
265+
Verify(QRCodeGenerator.ECCLevel.H, expectedH);
266+
267+
268+
void Verify(QRCodeGenerator.ECCLevel eccLevel, string expected)
269+
{
270+
var payload = new SamplePayload(stringValue, eccLevel);
271+
Encode(QRCodeGenerator.GenerateQrCode(payload)).ShouldBe(expected);
272+
foreach (var ecc in Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast<QRCodeGenerator.ECCLevel>())
273+
{
274+
if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default)
275+
Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected);
276+
else
277+
Should.Throw<ArgumentOutOfRangeException>(() => Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)));
278+
}
279+
}
280+
281+
string Encode(QRCodeData qrData)
282+
{
283+
return string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray());
284+
}
285+
}
286+
287+
private class SamplePayload : PayloadGenerator.Payload
288+
{
289+
private string _data;
290+
private QRCodeGenerator.ECCLevel _eccLevel;
291+
292+
public SamplePayload(string data, QRCodeGenerator.ECCLevel eccLevel)
293+
{
294+
_data = data;
295+
_eccLevel = eccLevel;
296+
}
297+
298+
public override QRCodeGenerator.ECCLevel EccLevel => _eccLevel;
299+
300+
public override string ToString() => _data;
301+
}
229302
}
230303

231304
public static class ExtensionMethods

0 commit comments

Comments
 (0)