npn47_iso8583 0.1.5.a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,419 @@
1
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
2
+ # MIT License, for details, see the LICENSE file accompaning
3
+ # this distribution
4
+
5
+ module ISO8583
6
+
7
+ # The class `Message` defines functionality to describe classes
8
+ # representing different type of messages, or message families.
9
+ # A message family consists of a number of possible message types that
10
+ # are allowed, and a way of naming and encoding the bitmaps allowed in
11
+ # the messages.
12
+ #
13
+ # To create your own message, start by subclassing Message:
14
+ #
15
+ # class MyMessage < Message
16
+ # (...)
17
+ # end
18
+ #
19
+ # the subtyped message should be told how the MTI is encoded:
20
+ #
21
+ # class MyMessage < Message
22
+ # mti_format N, :length => 4
23
+ # (...)
24
+ # end
25
+ #
26
+ # `N` above is an instance of Field which encodes numbers into their
27
+ # ASCII representations in a fixed length field. The option `length=>4`
28
+ # indicates the length of the fixed field.
29
+ #
30
+ # Next, the allowed message types are specified:
31
+ #
32
+ # class MyMessage < Message
33
+ # (...)
34
+ # mti 1100, "Authorization Request Acquirer Gateway"
35
+ # mti 1110, "Authorization Request Response Issuer Gateway"
36
+ # (...)
37
+ # end
38
+ #
39
+ # This basically defines to message types, 1100 and 1110 which may
40
+ # be accessed later either via their name or value:
41
+ #
42
+ # mes = MyMessage.new 1100
43
+ #
44
+ # or
45
+ # mes = MyMessage.new "Authorization Request Acquirer Gateway"
46
+ #
47
+ # or
48
+ # mes = MyMessage.new
49
+ # mes.mti = 1110 # or Auth. Req. Acq. Gateway ...
50
+ #
51
+ # Finally the allowed bitmaps, their names and the encoding rules are
52
+ # specified:
53
+ #
54
+ # class MyMessage < Message
55
+ # (...)
56
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
57
+ # bmp 3, "Processing Code", N, :length => 6
58
+ # bmp 4, "Amount (Transaction)", N, :length => 12
59
+ # bmp 6, "Amount, Cardholder Billing" , N, :length => 12
60
+ # (...)
61
+ # end
62
+ #
63
+ # The example above defines four bitmaps (2,3,4 and 6), and provides
64
+ # their bitmap number and description. The PAN field is variable length
65
+ # encoded (LL length indicator, ASCII, contents numeric, ASCII) and the
66
+ # maximum length of the field is limited to 19 using options.
67
+ #
68
+ # The other fields are fixed length numeric ASCII fields (the length of the fields is
69
+ # indicated by the `:length` options.)
70
+ #
71
+ # This message may be used as follows in order to interpret a received message.:
72
+ #
73
+ # mes = MyMessage.parse inputData
74
+ # puts mes[2] # prints the PAN from the message.
75
+ #
76
+ # Constructing own messages works as follows:
77
+ #
78
+ # mes = MyMessage.new 1100
79
+ # mes[2]= 474747474747
80
+ # # Alternatively
81
+ # mes["Primary Account Number (PAN)"]= 4747474747
82
+ # mes[3] = 1234 # padding is added by the Field en/decoder
83
+ # mes["Amount (Transaction)"] = 100
84
+ # mes[6] = 200
85
+ #
86
+ # the convenience method bmp_alias may be used in defining the class in
87
+ # order to provide direct access to fields using methods:
88
+ #
89
+ # class MyMessage < Message
90
+ # (...)
91
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
92
+ # (...)
93
+ # bmp_alias 2, :pan
94
+ # end
95
+ #
96
+ # this allows accessing fields in the following manner:
97
+ #
98
+ # mes = MyMessage.new 1100
99
+ # mes.pan = 474747474747
100
+ # puts mes.pan
101
+ # # Identical functionality to:
102
+ # mes[2]= 474747474747
103
+ # # or:
104
+ # mes["Primary Account Number (PAN)"]= 4747474747
105
+ #
106
+ # Most of the work in implementing a new set of message type lays in
107
+ # figuring out the correct fields to use defining the Message class via
108
+ # bmp.
109
+ #
110
+ class Message
111
+
112
+ # The value of the MTI (Message Type Indicator) of this message.
113
+ attr_reader :mti
114
+
115
+ # Instantiate a new instance of this type of Message
116
+ # optionally specifying an mti.
117
+ def initialize(mti = nil)
118
+ # values is an internal field used to collect all the
119
+ # bmp number | bmp name | field en/decoders | values
120
+ # which are set in this message.
121
+ @values = {}
122
+ self.mti = mti if mti
123
+ end
124
+
125
+ # Set the mti of the Message using either the actual value
126
+ # or the name of the message type that was defined using
127
+ # Message.mti
128
+ #
129
+ # === Example
130
+ # class MyMessage < Message
131
+ # (...)
132
+ # mti 1100, "Authorization Request Acquirer Gateway"
133
+ # end
134
+ #
135
+ # mes = MyMessage.new
136
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
137
+ def mti=(value)
138
+ num, name = _get_mti_definition(value)
139
+ @mti = num
140
+ end
141
+
142
+ # Set a field in this message, `key` is either the
143
+ # bmp number or it's name.
144
+ # ===Example
145
+ #
146
+ # mes = BlaBlaMessage.new
147
+ # mes[2]=47474747 # bmp 2 is generally the PAN
148
+ # mes["Primary Account Number"]=47474747 # if thats what you called the field in Message.bmp.
149
+ def []=(key, value)
150
+ bmp_def = _get_definition key
151
+ bmp_def.value = value
152
+ @values[bmp_def.bmp] = bmp_def
153
+ end
154
+
155
+ # Retrieve the decoded value of the contents of a bitmap
156
+ # described either by the bitmap number or name.
157
+ #
158
+ # ===Example
159
+ #
160
+ # mes = BlaBlaMessage.parse someMessageBytes
161
+ # mes[2] # bmp 2 is generally the PAN
162
+ # mes["Primary Account Number"] # if thats what you called the field in Message.bmp.
163
+ def [](key)
164
+ bmp_def = _get_definition key
165
+ bmp = @values[bmp_def.bmp]
166
+ bmp ? bmp.value : nil
167
+ end
168
+
169
+ # Retrieve the byte representation of the bitmap.
170
+ def to_b
171
+ raise ISO8583Exception.new "no MTI set!" unless mti
172
+ mti_enc = self.class._mti_format.encode(mti)
173
+ mti_enc << _body.join
174
+ end
175
+
176
+ # Returns a nicely formatted representation of this
177
+ # message.
178
+ def to_s
179
+ _mti_name = _get_mti_definition(mti)[1]
180
+ str = "MTI:#{mti} (#{_mti_name})\n\n"
181
+ _max = @values.values.max {|a,b|
182
+ a.name.length <=> b.name.length
183
+ }
184
+ _max_name = _max.name.length
185
+
186
+ @values.keys.sort.each{|bmp_num|
187
+ _bmp = @values[bmp_num]
188
+ str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
189
+ }
190
+ str
191
+ end
192
+
193
+
194
+ # METHODS starting with an underscore are meant for
195
+ # internal use only ...
196
+
197
+ # Returns an array of two byte arrays:
198
+ # [bitmap_bytes, message_bytes]
199
+ def _body
200
+ bitmap = Bitmap.new
201
+ message = ""
202
+ @values.keys.sort.each do |bmp_num|
203
+ bitmap.set(bmp_num)
204
+ enc_value = @values[bmp_num].encode
205
+ message << enc_value
206
+ end
207
+ [ bitmap.to_bytes, message ]
208
+ end
209
+
210
+ def _get_definition(key) #:nodoc:
211
+ b = self.class._definitions[key]
212
+ unless b
213
+ raise ISO8583Exception.new "no definition for field: #{key}"
214
+ end
215
+ b.dup
216
+ end
217
+
218
+ # return [mti_num, mti_value] for key being either
219
+ # mti_num or mti_value
220
+ def _get_mti_definition(key)
221
+ num_hash,name_hash = self.class._mti_definitions
222
+ if num_hash[key]
223
+ [key, num_hash[key]]
224
+ elsif name_hash[key]
225
+ [name_hash[key], key]
226
+ else
227
+ raise ISO8583Exception.new("MTI: #{key} not allowed!")
228
+ end
229
+ end
230
+
231
+ class << self
232
+
233
+ # Defines how the message type indicator is encoded into bytes.
234
+ # ===Params:
235
+ # * field : the decoder/encoder for the MTI
236
+ # * opts : the options to pass to this field
237
+ #
238
+ # === Example
239
+ # class MyMessage < Message
240
+ # mti_format N, :length =>4
241
+ # (...)
242
+ # end
243
+ #
244
+ # encodes the mti of this message using the `N` field (fixed
245
+ # length, plain ASCII) and sets the fixed lengh to 4 bytes.
246
+ #
247
+ # See also: mti
248
+ def mti_format(field, opts)
249
+ f = field.dup
250
+ _handle_opts(f, opts)
251
+ @mti_format = f
252
+ end
253
+
254
+ # Defines the message types allowed for this type of message and
255
+ # gives them names
256
+ #
257
+ # === Example
258
+ # class MyMessage < Message
259
+ # (...)
260
+ # mti 1100, "Authorization Request Acquirer Gateway"
261
+ # end
262
+ #
263
+ # mes = MyMessage.new
264
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
265
+ #
266
+ # See Also: mti_format
267
+ def mti(value, name)
268
+ @mtis_v ||= {}
269
+ @mtis_n ||= {}
270
+ @mtis_v[value] = name
271
+ @mtis_n[name] = value
272
+ end
273
+
274
+ # Define a bitmap in the message
275
+ # ===Params:
276
+ # * bmp : bitmap number
277
+ # * name : human readable form
278
+ # * field : field for encoding/decoding
279
+ # * opts : options to pass to the field, e.g. length for fxed len fields.
280
+ #
281
+ # ===Example
282
+ #
283
+ # class MyMessage < Message
284
+ # bmp 2, "PAN", LLVAR_N, :max =>19
285
+ # (...)
286
+ # end
287
+ #
288
+ # creates a class MyMessage that allows for a bitmap 2 which
289
+ # is named "PAN" and encoded by an LLVAR_N Field. The maximum
290
+ # length of the value is 19. This class may be used as follows:
291
+ #
292
+ # mes = MyMessage.new
293
+ # mes[2] = 474747474747 # or mes["PAN"] = 4747474747
294
+ #
295
+ def bmp(bmp, name, field, opts = nil)
296
+ @defs ||= {}
297
+
298
+ field = field.dup
299
+ field.name = name
300
+ field.bmp = bmp
301
+ _handle_opts(field, opts) if opts
302
+
303
+ bmp_def = BMP.new bmp, name, field
304
+
305
+ @defs[bmp] = bmp_def
306
+ @defs[name] = bmp_def
307
+ end
308
+
309
+ # Create an alias to access bitmaps directly using a method.
310
+ # Example:
311
+ # class MyMessage < Message
312
+ # (...)
313
+ # bmp 2, "PAN", LLVAR_N
314
+ # (...)
315
+ # bmp_alias 2, :pan
316
+ # end #class
317
+ #
318
+ # would allow you to access the PAN like this:
319
+ #
320
+ # mes.pan = 1234
321
+ # puts mes.pan
322
+ #
323
+ # instead of:
324
+ #
325
+ # mes[2] = 1234
326
+ #
327
+ def bmp_alias(bmp, aliaz)
328
+ define_method (aliaz) {
329
+ bmp_ = @values[bmp]
330
+ bmp_ ? bmp_.value : nil
331
+ }
332
+
333
+ define_method ("#{aliaz}=") {|value|
334
+ self[bmp] = value
335
+ # bmp_def = _get_definition(bmp)
336
+ # bmp_def.value= value
337
+ # @values[bmp] = bmp_def
338
+ }
339
+ end
340
+
341
+ # Parse the bytes `str` returning a message of the defined type.
342
+ def parse(str)
343
+ message = self.new
344
+ message.mti, rest = _mti_format.parse(str)
345
+ bmp,rest = Bitmap.parse(rest)
346
+ bmp.each {|bit|
347
+ bmp_def = _definitions[bit]
348
+ value, rest = bmp_def.field.parse(rest)
349
+ message[bit] = value
350
+ }
351
+ message
352
+ end
353
+
354
+ # access the mti definitions applicable to the Message
355
+ #
356
+ # returns a pair of hashes containing:
357
+ #
358
+ # mti_value => mti_name
359
+ #
360
+ # mti_name => mti_value
361
+ #
362
+ def _mti_definitions
363
+ [@mtis_v, @mtis_n]
364
+ end
365
+
366
+ # Access the field definitions of this class, this is a
367
+ # hash containing [bmp_number, BMP] and [bitmap_name, BMP]
368
+ # pairs.
369
+ #
370
+ def _definitions
371
+ @defs
372
+ end
373
+
374
+ # Returns the field definition to format the mti.
375
+ def _mti_format
376
+ @mti_format
377
+ end
378
+
379
+
380
+
381
+ # METHODS starting with an underscore are meant for
382
+ # internal use only ...
383
+
384
+ # Modifies the field definitions of the fields passed
385
+ # in through the `bmp` and `mti_format` class methods.
386
+ #
387
+ def _handle_opts(field, opts)
388
+ opts.each_pair {|key, value|
389
+ key = (key.to_s+"=").to_sym
390
+ if field.respond_to?(key)
391
+ field.send(key, value)
392
+ else
393
+ warn "unknown option #{key} for #{field.name}"
394
+ end
395
+ }
396
+ end
397
+ end
398
+ end
399
+
400
+ # Internal class used to tie together name, bitmap number, field en/decoder
401
+ # and the value of the corresponding field
402
+ class BMP
403
+ attr_accessor :bmp
404
+ attr_accessor :name
405
+ attr_accessor :field
406
+ attr_accessor :value
407
+
408
+ def initialize(bmp, name, field)
409
+ @bmp = bmp
410
+ @name = name
411
+ @field = field
412
+ end
413
+
414
+ def encode
415
+ field.encode(value)
416
+ end
417
+ end
418
+
419
+ end
@@ -0,0 +1,94 @@
1
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
2
+ # MIT License, for details, see the LICENSE file accompaning
3
+ # this distribution
4
+
5
+ module ISO8583
6
+
7
+ # general utilities
8
+ #
9
+ # Convert a String of bytes to their hex representation
10
+ # E.g.:
11
+ #
12
+ # b2hex "\x12\x34\xab" => "1234AB"
13
+ #
14
+ def b2hex(byte_string)
15
+ r = byte_string.unpack("H*")[0]
16
+ r.length > 1 ? r : " "
17
+ end
18
+
19
+ #
20
+ # Convert a String containing hex data to
21
+ # a String containing the corresponding bytes:
22
+ #
23
+ # hex2b "abcd12" => "\xa\cd\12"
24
+ #
25
+ def hex2b(hex_string)
26
+ string = hex_string.gsub(/\s+/, "")
27
+ raise ISO8583Exception.new("Invalid Hex chars: #{hex_string}") unless string =~ /^[A-Fa-f0-9]*$/
28
+ raise ISO8583Exception.new("Uneven number of Hex chars #{hex_string}") unless ( (string.length % 2) == 0)
29
+ [string].pack("H*")
30
+ end
31
+
32
+ def _conv(str, mapping)
33
+ _str = ""
34
+ str.each_byte{|byte|
35
+ _str << mapping[byte]
36
+ }
37
+ _str
38
+ end
39
+
40
+ #
41
+ # Convert a String of ASCII chars to EBCDIC
42
+ #
43
+ def ascii2ebcdic(ascii)
44
+ _conv(ascii, US_ASCII2IBM037)
45
+ end
46
+
47
+ #
48
+ # Convert an EBCDIC String to ASCII
49
+ #
50
+ def ebcdic2ascii(ebcdic)
51
+ _conv(ebcdic, IBM0372US_ASCII)
52
+ end
53
+
54
+ # The charsets supported by iconv aren't guranteed. At the very least MACs don't support ebcdic,
55
+ # so providing rudimentary mappings here.
56
+ US_ASCII2IBM037 = [
57
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
58
+ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
59
+ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
60
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
61
+ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
62
+ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xba, 0xe0, 0xbb, 0xb0, 0x6d,
63
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
64
+ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07,
65
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
66
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
67
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
68
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
69
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
70
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
71
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
72
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
73
+ ]
74
+
75
+ IBM0372US_ASCII = [
76
+ 0x00, 0x01, 0x02, 0x03, 0x3f, 0x09, 0x3f, 0x7f, 0x3f, 0x3f, 0x3f, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
77
+ 0x10, 0x11, 0x12, 0x13, 0x3f, 0x0a, 0x08, 0x3f, 0x18, 0x19, 0x3f, 0x3f, 0x1c, 0x1d, 0x1e, 0x1f,
78
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x0a, 0x17, 0x1b, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x05, 0x06, 0x07,
79
+ 0x3f, 0x3f, 0x16, 0x3f, 0x3f, 0x3f, 0x3f, 0x04, 0x3f, 0x3f, 0x3f, 0x3f, 0x14, 0x15, 0x3f, 0x1a,
80
+ 0x20, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
81
+ 0x26, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x3f,
82
+ 0x2d, 0x2f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
83
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
84
+ 0x3f, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
85
+ 0x3f, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
86
+ 0x3f, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
87
+ 0x5e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x5b, 0x5d, 0x3f, 0x3f, 0x3f, 0x3f,
88
+ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
89
+ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
90
+ 0x5c, 0x3f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
91
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
92
+ ]
93
+
94
+ end
@@ -0,0 +1,4 @@
1
+ # -*- mode: ruby; encoding: utf-8; tab-width: 2; indent-tabs-mode: nil -*-
2
+ module ISO8583
3
+ VERSION = "0.1.5.a"
4
+ end
data/lib/iso8583.rb ADDED
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module ISO8583
5
+ require "iso8583/field"
6
+ require "iso8583/codec"
7
+ require "iso8583/fields"
8
+ require "iso8583/exception"
9
+ require "iso8583/bitmap"
10
+ require "iso8583/message"
11
+ require "iso8583/util"
12
+ require "iso8583/version"
13
+ end
@@ -0,0 +1,80 @@
1
+ require 'lib/iso8583'
2
+ require 'test/unit'
3
+
4
+ include ISO8583
5
+
6
+ class BitmapTests < Test::Unit::TestCase
7
+ def test_create_empty
8
+ b = Bitmap.new
9
+ assert_equal(b.to_s.size, 64, "proper length: 64")
10
+ b.set(112)
11
+ assert_equal(b.to_s.size, 128, "proper length: 128")
12
+ assert_equal(b.to_s, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000")
13
+ b.unset(112)
14
+ 5.step(20,2){|i| b.set(i)}
15
+ assert(b.to_s.size==64, "proper length: 64")
16
+ assert_equal(b.to_s, "0000101010101010101000000000000000000000000000000000000000000000")
17
+ assert b[5]
18
+ assert b[7]
19
+ assert b[11]
20
+ assert !b[12]
21
+ assert !b[199]
22
+
23
+ assert_raises(ISO8583Exception) {b.set 1000 }
24
+ assert_raises(ISO8583Exception) { b.set 1 }
25
+ assert_raises(ISO8583Exception) { b.set -1 }
26
+ end
27
+
28
+ def test_out_of_bounds_errors
29
+ b = Bitmap.new
30
+ assert_raises(ISO8583Exception) {b.set 1000 }
31
+ assert_raises(ISO8583Exception) { b.set 1 }
32
+ assert_raises(ISO8583Exception) { b.set -1 }
33
+ end
34
+
35
+ def test_parse_bmp
36
+ # 0000000001001001001001001001001001001001001001001001001001000000
37
+ # generated by: 10.step(60,3) {|i| mp.set(i)}
38
+
39
+ tst = "\x00\x49\x24\x92\x49\x24\x92\x40"
40
+ b = Bitmap.new tst
41
+ 10.step(60,3) {|i|
42
+ assert(b[i], "bit #{i} is not set.")
43
+ assert(!b[i+i], "bit #{i+i} is set.")
44
+ }
45
+
46
+ #10000000000000000001000000100000010000001000000100000010000001000000100000010000001000000100000010000001000000100000010000001000
47
+ # generated by: 20.step(128,7) {|i| mp.set(i)}
48
+ tst = "\x80\x00\x10\x20\x40\x81\x02\x04\x08\x10\x20\x40\x81\x02\x04\x08"
49
+ b = Bitmap.new tst
50
+ 20.step(128,7) {|i|
51
+ assert(b[i], "bit #{i} is not set. (128 bit map)")
52
+ assert(!b[i+i], "bit #{i+i} is set. (128 bit map)")
53
+ }
54
+ end
55
+
56
+ def test_parse_rest
57
+ tst = "\x00\x49\x24\x92\x49\x24\x92\x40\x31\x32\x33\x34"
58
+ b, rest = Bitmap.parse tst
59
+ 10.step(60,3) {|i|
60
+ assert(b[i], "bit #{i} is not set.")
61
+ assert(!b[i+i], "bit #{i+i} is set.")
62
+ }
63
+ assert_equal("1234", rest)
64
+ end
65
+
66
+ def test_each
67
+ bmp = Bitmap.new
68
+ bmp.set(2)
69
+ bmp.set(3)
70
+ bmp.set(5)
71
+ bmp.set(6)
72
+ arr = []
73
+ bmp.each{|bit|
74
+ arr.push bit
75
+ }
76
+ assert_equal [2,3,5,6], arr
77
+ end
78
+
79
+ end
80
+