File: boundary_test.rb

package info (click to toggle)
ruby-rqrcode-core 2.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 520 kB
  • sloc: ruby: 2,289; makefile: 4; sh: 4
file content (279 lines) | stat: -rw-r--r-- 10,671 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
require "test_helper"

# Tests for boundary conditions, edge cases, and maximum capacity scenarios
class BoundaryTest < Minitest::Test
  def test_empty_string
    # Empty string should work at all error correction levels
    %i[l m q h].each do |level|
      qr = RQRCodeCore::QRCode.new("", level: level)
      assert qr.version >= 1, "Empty string should produce valid QR code at level #{level}"
      assert qr.modules.size > 0, "Empty string should have modules at level #{level}"
    end
  end

  def test_single_character
    # Single characters in each mode
    numeric_qr = RQRCodeCore::QRCode.new("0")
    alpha_qr = RQRCodeCore::QRCode.new("A")
    byte_qr = RQRCodeCore::QRCode.new("a")

    assert_equal 1, numeric_qr.version, "Single digit should fit in version 1"
    assert_equal 1, alpha_qr.version, "Single alpha should fit in version 1"
    assert_equal 1, byte_qr.version, "Single byte should fit in version 1"
  end

  def test_all_numeric_digits
    qr = RQRCodeCore::QRCode.new("0123456789")
    assert_equal :mode_number, qr.mode, "All digits should use numeric mode"
    assert qr.modules.size > 0, "Should produce valid QR code"
  end

  def test_all_alphanumeric_characters
    # Valid alphanumeric characters: 0-9, A-Z, space, and $%*+-./:
    alpha_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
    qr = RQRCodeCore::QRCode.new(alpha_chars)
    assert_equal :mode_alpha_numk, qr.mode, "Valid alphanumeric chars should use alphanumeric mode"
  end

  def test_boundary_between_numeric_and_alphanumeric
    # Just digits: should be numeric
    assert_equal :mode_number, RQRCodeCore::QRCode.new("123456").mode

    # Digits with uppercase letter: should be alphanumeric
    assert_equal :mode_alpha_numk, RQRCodeCore::QRCode.new("123456A").mode

    # Digits with space (valid alphanumeric): should be alphanumeric
    assert_equal :mode_alpha_numk, RQRCodeCore::QRCode.new("123 456").mode
  end

  def test_boundary_between_alphanumeric_and_byte
    # Uppercase only: should be alphanumeric
    assert_equal :mode_alpha_numk, RQRCodeCore::QRCode.new("HELLO").mode

    # With lowercase: should be byte
    assert_equal :mode_8bit_byte, RQRCodeCore::QRCode.new("Hello").mode

    # With valid alphanumeric punctuation: should be alphanumeric
    assert_equal :mode_alpha_numk, RQRCodeCore::QRCode.new("HELLO-WORLD").mode

    # With invalid alphanumeric punctuation: should be byte
    assert_equal :mode_8bit_byte, RQRCodeCore::QRCode.new("HELLO_WORLD").mode
  end

  def test_maximum_version_40
    # Version 40 is the maximum QR code version
    qr = RQRCodeCore::QRCode.new("test", size: 40)
    assert_equal 40, qr.version, "Should support version 40"
    assert_equal 177, qr.module_count, "Version 40 should be 177x177 (40*4+17)"
  end

  def test_version_boundary_transitions
    # Test data lengths that sit near version boundaries
    # These lengths are chosen to be near capacity limits for version 1

    # Version 1 numeric capacity at level H: 17 digits
    assert_equal 1, RQRCodeCore::QRCode.new("1" * 17, level: :h).version
    # One more should bump to version 2
    assert_equal 2, RQRCodeCore::QRCode.new("1" * 18, level: :h).version

    # Version 1 alphanumeric capacity at level H: 10 characters
    assert_equal 1, RQRCodeCore::QRCode.new("A" * 10, level: :h).version
    # One more should bump to version 2
    assert_equal 2, RQRCodeCore::QRCode.new("A" * 11, level: :h).version

    # Version 1 byte capacity at level H: 7 bytes
    assert_equal 1, RQRCodeCore::QRCode.new("a" * 7, level: :h).version
    # One more should bump to version 2
    assert_equal 2, RQRCodeCore::QRCode.new("a" * 8, level: :h).version
  end

  def test_exact_capacity_for_version_1_all_levels
    # Test exact maximum capacity for version 1 at each error correction level

    # Level L: 25 alphanumeric characters
    assert_equal 1, RQRCodeCore::QRCode.new("A" * 25, level: :l).version

    # Level M: 20 alphanumeric characters
    assert_equal 1, RQRCodeCore::QRCode.new("A" * 20, level: :m).version

    # Level Q: 16 alphanumeric characters
    assert_equal 1, RQRCodeCore::QRCode.new("A" * 16, level: :q).version

    # Level H: 10 alphanumeric characters
    assert_equal 1, RQRCodeCore::QRCode.new("A" * 10, level: :h).version
  end

  def test_long_numeric_string
    # Test very long numeric strings (tests numeric mode efficiency)
    long_numeric = "1" * 500
    qr = RQRCodeCore::QRCode.new(long_numeric, level: :l)

    assert_equal :mode_number, qr.mode, "Long numeric string should use numeric mode"
    assert qr.version < 15, "500 digits should fit in a reasonably small version"
  end

  def test_long_alphanumeric_string
    # Test very long alphanumeric strings
    long_alpha = "A" * 300
    qr = RQRCodeCore::QRCode.new(long_alpha, level: :l)

    assert_equal :mode_alpha_numk, qr.mode, "Long alphanumeric string should use alphanumeric mode"
    assert qr.version < 20, "300 alphanumeric chars should fit in a reasonably small version"
  end

  def test_long_byte_string
    # Test very long byte strings
    long_byte = "a" * 200
    qr = RQRCodeCore::QRCode.new(long_byte, level: :l)

    assert_equal :mode_8bit_byte, qr.mode, "Long byte string should use byte mode"
    assert qr.version < 15, "200 bytes should fit in a reasonably small version"
  end

  def test_very_long_string_automatically_selects_version
    # Don't specify size, let it auto-select
    very_long = "x" * 1000
    qr = RQRCodeCore::QRCode.new(very_long, level: :l)

    assert qr.version > 1, "Very long string should require version > 1"
    assert qr.version <= 40, "Should not exceed maximum version 40"
  end

  def test_minimum_version_parameter
    # When size is not specified, should use minimum version needed
    short_data = "hi"
    qr = RQRCodeCore::QRCode.new(short_data)

    assert_equal 1, qr.version, "Short data should default to version 1"
  end

  def test_forced_larger_version
    # Should be able to force a larger version than needed
    short_data = "hi"
    qr = RQRCodeCore::QRCode.new(short_data, size: 10)

    assert_equal 10, qr.version, "Should respect forced larger version"
    assert_equal 57, qr.module_count, "Version 10 should be 57x57"
  end

  def test_max_size_parameter
    # max_size should limit the version selection
    long_data = "x" * 1000

    # Without limit, should work
    qr_unlimited = RQRCodeCore::QRCode.new(long_data, level: :l)
    assert qr_unlimited.version > 10, "Long data should need large version"

    # With reasonable max_size, should work
    qr_limited = RQRCodeCore::QRCode.new(long_data, level: :l, max_size: 30)
    assert qr_limited.version <= 30, "Should respect max_size limit"
  end

  def test_data_too_long_for_max_size_raises_error
    # Data that's too long for the max_size should raise error
    very_long_data = "x" * 2000

    assert_raises(RQRCodeCore::QRCodeRunTimeError) do
      RQRCodeCore::QRCode.new(very_long_data, level: :h, max_size: 10)
    end
  end

  def test_zero_digit
    qr = RQRCodeCore::QRCode.new("0")
    assert_equal :mode_number, qr.mode, "Single zero should use numeric mode"
    assert qr.modules.size > 0, "Zero should produce valid QR code"
  end

  def test_large_numbers
    # Test very large numeric values
    large_num = "9" * 100
    qr = RQRCodeCore::QRCode.new(large_num)

    assert_equal :mode_number, qr.mode, "Large numeric string should use numeric mode"
    assert qr.modules.size > 0, "Should produce valid QR code"
  end

  def test_numeric_with_leading_zeros
    qr = RQRCodeCore::QRCode.new("00012345")
    assert_equal :mode_number, qr.mode, "Numbers with leading zeros should use numeric mode"
  end

  def test_all_zeros
    qr = RQRCodeCore::QRCode.new("00000000")
    assert_equal :mode_number, qr.mode, "All zeros should use numeric mode"
    assert qr.modules.size > 0, "Should produce valid QR code"
  end

  def test_repeated_characters
    # Test strings with repeated characters (good for compression testing)
    repeated = "A" * 100
    qr = RQRCodeCore::QRCode.new(repeated)

    assert qr.modules.size > 0, "Repeated characters should produce valid QR code"
    assert qr.version <= 40, "Should not exceed maximum version"
  end

  def test_alternating_characters
    # Test strings that don't compress well
    alternating = ("AB" * 50)
    qr = RQRCodeCore::QRCode.new(alternating)

    assert qr.modules.size > 0, "Alternating characters should produce valid QR code"
  end

  def test_checked_with_boundary_coordinates
    qr = RQRCodeCore::QRCode.new("test", size: 5)
    max = qr.module_count - 1

    # Should work with valid boundary coordinates
    qr.checked?(0, 0)      # Top-left corner should be valid
    qr.checked?(0, max)    # Top-right corner should be valid
    qr.checked?(max, 0)    # Bottom-left corner should be valid
    qr.checked?(max, max)  # Bottom-right corner should be valid

    # Should raise error with out-of-bounds coordinates
    assert_raises(RQRCodeCore::QRCodeRunTimeError) { qr.checked?(-1, 0) }
    assert_raises(RQRCodeCore::QRCodeRunTimeError) { qr.checked?(0, -1) }
    assert_raises(RQRCodeCore::QRCodeRunTimeError) { qr.checked?(qr.module_count, 0) }
    assert_raises(RQRCodeCore::QRCodeRunTimeError) { qr.checked?(0, qr.module_count) }
  end

  def test_url_maximum_length
    # URLs are common QR code content, test reasonable max lengths
    base_url = "https://example.com/path"

    # Short URL should work
    qr_short = RQRCodeCore::QRCode.new(base_url)
    assert qr_short.modules.size > 0

    # Long URL with query params should work
    long_url = base_url + "?" + ("param=value&" * 50)
    qr_long = RQRCodeCore::QRCode.new(long_url, level: :l)
    assert qr_long.modules.size > 0
    assert qr_long.version <= 40
  end

  def test_mode_specification_overrides_auto_detection
    # Numeric string forced to alphanumeric mode
    qr = RQRCodeCore::QRCode.new("12345", mode: :alphanumeric)
    assert_equal :mode_alpha_numk, qr.mode, "Should respect forced mode"

    # Alphanumeric string forced to byte mode
    qr2 = RQRCodeCore::QRCode.new("HELLO", mode: :byte_8bit)
    assert_equal :mode_8bit_byte, qr2.mode, "Should respect forced byte mode"
  end

  def test_invalid_mode_for_data_raises_error
    # Lowercase cannot be encoded in alphanumeric mode
    assert_raises(RQRCodeCore::QRCodeArgumentError) do
      RQRCodeCore::QRCode.new("hello", mode: :alphanumeric)
    end
  end

  def test_numeric_mode_with_non_digits_raises_error
    # Non-digits cannot be encoded in numeric mode
    assert_raises(RQRCodeCore::QRCodeArgumentError) do
      RQRCodeCore::QRCode.new("123abc", mode: :number)
    end
  end
end