client_for_poslynx 0.2.4 → 0.2.5
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.
- checksums.yaml +8 -8
- data/CHANGELOG +6 -0
- data/README.md +3 -0
- data/foo.svg +20 -0
- data/lib/client_for_poslynx/bit_sequence.rb +139 -0
- data/lib/client_for_poslynx/data/requests/can_visit.rb +1 -0
- data/lib/client_for_poslynx/data/requests/credit_card_sale.rb +1 -0
- data/lib/client_for_poslynx/data/requests/pin_pad_get_signature.rb +17 -0
- data/lib/client_for_poslynx/data/requests.rb +1 -0
- data/lib/client_for_poslynx/data/responses/credit_card_sale.rb +1 -0
- data/lib/client_for_poslynx/data/responses/pin_pad_get_signature.rb +18 -0
- data/lib/client_for_poslynx/data/responses.rb +1 -0
- data/lib/client_for_poslynx/experimental.rb +16 -0
- data/lib/client_for_poslynx/has_client_console_support.rb +7 -1
- data/lib/client_for_poslynx/message_handling/xml_extractor.rb +3 -2
- data/lib/client_for_poslynx/signature_image/draw.rb +43 -0
- data/lib/client_for_poslynx/signature_image/move.rb +43 -0
- data/lib/client_for_poslynx/signature_image/to_svg_converter.rb +99 -0
- data/lib/client_for_poslynx/signature_image.rb +97 -0
- data/lib/client_for_poslynx/version.rb +1 -1
- data/spec/client_for_poslynx/bit_sequence_spec.rb +147 -0
- data/spec/client_for_poslynx/data/requests/credit_card_sale_spec.rb +15 -11
- data/spec/client_for_poslynx/data/requests/pin_pad_get_signature_spec.rb +72 -0
- data/spec/client_for_poslynx/data/responses/credit_card_sale_spec.rb +4 -0
- data/spec/client_for_poslynx/data/responses/pin_pad_display_message_spec.rb +14 -18
- data/spec/client_for_poslynx/signature_image/to_svg_converter_spec.rb +114 -0
- data/spec/client_for_poslynx/signature_image_spec.rb +91 -0
- data/spec/spec_helper.rb +1 -0
- metadata +19 -2
@@ -0,0 +1,147 @@
|
|
1
|
+
# coding: binary
|
2
|
+
# Note that "binary" coding is important for this file
|
3
|
+
# since that allows us to write packed binary string
|
4
|
+
# literals.
|
5
|
+
|
6
|
+
require 'spec_helper'
|
7
|
+
require 'stringio'
|
8
|
+
|
9
|
+
module ClientForPoslynx
|
10
|
+
|
11
|
+
describe BitSequence do
|
12
|
+
subject{ klass ^ "Do" }
|
13
|
+
let( :klass ) { described_class }
|
14
|
+
|
15
|
+
specify "is unequal to another instance based on different packed bits" do
|
16
|
+
other_instance = klass ^ "Re"
|
17
|
+
expect( subject ).not_to eq( other_instance )
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "is equal to another instance based on same packed bits" do
|
21
|
+
other_instance = klass ^ "Do"
|
22
|
+
expect( subject ).to eq( other_instance )
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "is unequal to another instance based on a non-equivalent binary digit sequence" do
|
26
|
+
other_instance = klass / "1010101010101010"
|
27
|
+
expect( subject ).not_to eq( other_instance )
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "is equal to another instance based on an equivalent binary digit sequence" do
|
31
|
+
other_instance = klass / "0100010001101111"
|
32
|
+
expect( subject ).to eq( other_instance )
|
33
|
+
end
|
34
|
+
|
35
|
+
it "sets extra bits in final byte to zeros when packing bits" do
|
36
|
+
instance = klass / "01110000111"
|
37
|
+
expect( instance.to_packed_bits ).to eq( "\x70\xE0" )
|
38
|
+
end
|
39
|
+
|
40
|
+
it "correctly reports how many bits long it is" do
|
41
|
+
expect( klass.new_empty .length ).to eq( 0 )
|
42
|
+
expect( ( klass / '0' ).length ).to eq( 1 )
|
43
|
+
expect( ( klass / '10' ).length ).to eq( 2 )
|
44
|
+
expect( ( klass / ('01' * 9) ).length ).to eq( 18 )
|
45
|
+
end
|
46
|
+
|
47
|
+
it "correctly reports what its first bit-digit is" do
|
48
|
+
expect( ( klass / '101' ).first_bit_digit ).to eq( '1' )
|
49
|
+
expect( ( klass / '1101' ).first_bit_digit ).to eq( '1' )
|
50
|
+
expect( ( klass / '001' ).first_bit_digit ).to eq( '0' )
|
51
|
+
expect( ( klass / '0101' ).first_bit_digit ).to eq( '0' )
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can have another bit sequence pushed onto its end" do
|
55
|
+
actual = klass / '10101'
|
56
|
+
actual << klass / '001100'
|
57
|
+
expected = klass / '10101001100'
|
58
|
+
expect( actual ).to eq( expected )
|
59
|
+
end
|
60
|
+
|
61
|
+
it "can have a bit sequence shifted off of its front" do
|
62
|
+
bit_source = klass / '111000111000111000'
|
63
|
+
taken = bit_source.shift(9)
|
64
|
+
|
65
|
+
expected_remaining = klass / '000111000'
|
66
|
+
expected_taken = klass / '111000111'
|
67
|
+
|
68
|
+
expect( bit_source ).to eq( expected_remaining )
|
69
|
+
expect( taken ).to eq( expected_taken )
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can have another bit sequence unshifted onto its fron" do
|
73
|
+
bit_target = klass / '000111000'
|
74
|
+
bit_target.unshift klass / '111000111'
|
75
|
+
|
76
|
+
expected_resulting = klass / '111000111000111000'
|
77
|
+
|
78
|
+
expect( bit_target ).to eq( expected_resulting )
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can be (big-endian) interpreted as an unsigned integer" do
|
82
|
+
expect( ( klass / '111000011110000' ).as_unsigned ).to eq( 0x70F0 )
|
83
|
+
expect( ( klass / ( '11110000' * 8 ) ).as_unsigned ).to eq( 0xF0F0F0F0F0F0F0F0 )
|
84
|
+
end
|
85
|
+
|
86
|
+
it "fails to be interpreted as unsigned if longer than 64 bits" do
|
87
|
+
bit_seq = klass / ( '11110000' * 8 + '1' )
|
88
|
+
expect{ bit_seq.as_unsigned }.to \
|
89
|
+
raise_exception( klass::TooManyBitsLong, /\b64\b/ )
|
90
|
+
end
|
91
|
+
|
92
|
+
it "can be (big-endian) interpreted as the sign-bit and magnitude of an integer" do
|
93
|
+
expect( ( klass / '000' ).as_sign_and_magnitude ).to eq( 0 )
|
94
|
+
expect( ( klass / '0111000011110000' ).as_sign_and_magnitude ).to eq( 0x70F0 )
|
95
|
+
expect( ( klass / '1111000011110000' ).as_sign_and_magnitude ).to eq( - 0x70F0 )
|
96
|
+
expect( ( klass / ('1' + '11110000' * 8) ).as_sign_and_magnitude ).to eq( - 0xF0F0F0F0F0F0F0F0 )
|
97
|
+
end
|
98
|
+
|
99
|
+
it "fails to be interpreted as sign-bit and magnitude if longer than 65 bits" do
|
100
|
+
bit_seq = klass / ( '0' + '11110000' * 8 + '1' )
|
101
|
+
expect{ bit_seq.as_sign_and_magnitude }.
|
102
|
+
to raise_exception( klass::TooManyBitsLong, /\b65\b/ )
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can be constructed as the big-endian representation of an unsigned integer" do
|
106
|
+
expect( klass.from_unsigned( 0, 9 ) ).to eq( klass / ( '0' * 9 ) )
|
107
|
+
expect( klass.from_unsigned( 0x23, 7 ) ).to eq( klass / ( '0100011' ) )
|
108
|
+
expect( klass.from_unsigned( (2 ** 64) - 1, 64 ) ).to eq( klass / ( '1' * 64 ) )
|
109
|
+
end
|
110
|
+
|
111
|
+
it "can be constructed as the sign-bit and big-endian magnitude representation of an integer" do
|
112
|
+
expect( klass.from_sign_and_magnitude_of( 0, 10 ) ).to eq( klass / ( '0' * 10 ) )
|
113
|
+
expect( klass.from_sign_and_magnitude_of( 0x23, 8 ) ).to eq( klass / ( '00100011' ) )
|
114
|
+
expect( klass.from_sign_and_magnitude_of( - 0x23, 8 ) ).to eq( klass / ( '10100011' ) )
|
115
|
+
expect( klass.from_sign_and_magnitude_of( -(2 ** 64) + 1, 65 ) ).to eq( klass / ( '1' * 65 ) )
|
116
|
+
end
|
117
|
+
|
118
|
+
it "fails to be constructed as a longer than 64-bit representation of an unsigned number" do
|
119
|
+
expect{ klass.from_unsigned(0x23, 65) }.
|
120
|
+
to raise_exception( klass::TooManyBitsLong, /\b64\b/ )
|
121
|
+
end
|
122
|
+
|
123
|
+
it "fails to be constructed as an unsigned representation a negative number" do
|
124
|
+
expect{ klass.from_unsigned(-1, 5) }.
|
125
|
+
to raise_exception( klass::NumberOutOfBounds )
|
126
|
+
end
|
127
|
+
|
128
|
+
it "fails to be constructed as an under-sized representation of an unsigned number" do
|
129
|
+
expect{ klass.from_unsigned(0x23, 5) }.
|
130
|
+
to raise_exception( klass::NumberOutOfBounds )
|
131
|
+
end
|
132
|
+
|
133
|
+
it "can be build by unpacking bits from a uuencoded string" do
|
134
|
+
actual = klass.from_uuencoded( "&,C(R,C(R\n" )
|
135
|
+
expected = klass ^ "\x32" * 6
|
136
|
+
|
137
|
+
expect( actual ).to eq( expected )
|
138
|
+
end
|
139
|
+
|
140
|
+
it "can be packed to a uuencoded string" do
|
141
|
+
bit_seq = klass ^ "\x32" * 6
|
142
|
+
expect( bit_seq.uuencode ).to eq( "&,C(R,C(R\n" )
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -27,6 +27,7 @@ module ClientForPoslynx
|
|
27
27
|
subject.track_2 = 'the-two'
|
28
28
|
subject.card_number = 'the-number'
|
29
29
|
subject.expiry_date = 'the-expiration'
|
30
|
+
subject.capture_signature = 'the-capture-signature'
|
30
31
|
|
31
32
|
expected_xml =
|
32
33
|
"<PLRequest>" +
|
@@ -42,6 +43,7 @@ module ClientForPoslynx
|
|
42
43
|
"<Track1>the-one</Track1>" +
|
43
44
|
"<CardNumber>the-number</CardNumber>" +
|
44
45
|
"<ExpiryDate>the-expiration</ExpiryDate>" +
|
46
|
+
"<ReqPPSigCapture>the-capture-signature</ReqPPSigCapture>" +
|
45
47
|
"</PLRequest>\n"
|
46
48
|
|
47
49
|
expect( subject.xml_serialize ).to eq( expected_xml )
|
@@ -75,22 +77,24 @@ module ClientForPoslynx
|
|
75
77
|
<Track1>the-one</Track1>
|
76
78
|
<CardNumber>the-number</CardNumber>
|
77
79
|
<ExpiryDate>the-expiration</ExpiryDate>
|
80
|
+
<ReqPPSigCapture>the-capture-signature</ReqPPSigCapture>
|
78
81
|
</PLRequest>
|
79
82
|
XML
|
80
83
|
|
81
84
|
actual_instance = described_class.xml_deserialize xml_input
|
82
85
|
|
83
|
-
expect( actual_instance.client_mac ).to eq( 'the-MAC'
|
84
|
-
expect( actual_instance.merchant_supplied_id ).to eq( 'the-transaction'
|
85
|
-
expect( actual_instance.client_id ).to eq( 'the-client'
|
86
|
-
expect( actual_instance.tax_amount ).to eq( 'the-tax'
|
87
|
-
expect( actual_instance.customer_code ).to eq( 'the-code'
|
88
|
-
expect( actual_instance.amount ).to eq( 'the-amount'
|
89
|
-
expect( actual_instance.input_source ).to eq( 'the-source'
|
90
|
-
expect( actual_instance.track_1 ).to eq( 'the-one'
|
91
|
-
expect( actual_instance.track_2 ).to eq( 'the-two'
|
92
|
-
expect( actual_instance.card_number ).to eq( 'the-number'
|
93
|
-
expect( actual_instance.expiry_date ).to eq( 'the-expiration'
|
86
|
+
expect( actual_instance.client_mac ).to eq( 'the-MAC' )
|
87
|
+
expect( actual_instance.merchant_supplied_id ).to eq( 'the-transaction' )
|
88
|
+
expect( actual_instance.client_id ).to eq( 'the-client' )
|
89
|
+
expect( actual_instance.tax_amount ).to eq( 'the-tax' )
|
90
|
+
expect( actual_instance.customer_code ).to eq( 'the-code' )
|
91
|
+
expect( actual_instance.amount ).to eq( 'the-amount' )
|
92
|
+
expect( actual_instance.input_source ).to eq( 'the-source' )
|
93
|
+
expect( actual_instance.track_1 ).to eq( 'the-one' )
|
94
|
+
expect( actual_instance.track_2 ).to eq( 'the-two' )
|
95
|
+
expect( actual_instance.card_number ).to eq( 'the-number' )
|
96
|
+
expect( actual_instance.expiry_date ).to eq( 'the-expiration' )
|
97
|
+
expect( actual_instance.capture_signature ).to eq( 'the-capture-signature' )
|
94
98
|
end
|
95
99
|
|
96
100
|
it "accepts a canonical visitor" do
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module ClientForPoslynx
|
6
|
+
|
7
|
+
describe Data::Requests::PinPadGetSignature do
|
8
|
+
|
9
|
+
it_behaves_like "a data object"
|
10
|
+
|
11
|
+
it "Serializes to a PLRequest XML document for a PPGETSIGNATURE request" do
|
12
|
+
mac = Data::Requests::DEFAULT_CLIENT_MAC
|
13
|
+
expected_xml =
|
14
|
+
"<PLRequest><Command>PPGETSIGNATURE</Command><ClientMAC>#{mac}</ClientMAC></PLRequest>\n"
|
15
|
+
|
16
|
+
expect( subject.xml_serialize ).to eq( expected_xml )
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
it "Serializes all assigned members to appropriate elements" do
|
21
|
+
subject.client_mac = 'the-mac'
|
22
|
+
|
23
|
+
expected_xml =
|
24
|
+
"<PLRequest>" +
|
25
|
+
"<Command>PPGETSIGNATURE</Command>" +
|
26
|
+
"<ClientMAC>the-mac</ClientMAC>" +
|
27
|
+
"</PLRequest>\n"
|
28
|
+
|
29
|
+
expect( subject.xml_serialize ).to eq( expected_xml )
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it "parses minimally acceptable XML data" do
|
34
|
+
xml_input = <<-XML
|
35
|
+
<PLRequest>
|
36
|
+
<Command>PPGETSIGNATURE</Command>
|
37
|
+
</PLRequest>
|
38
|
+
XML
|
39
|
+
|
40
|
+
actual_instance = described_class.xml_deserialize xml_input
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "parses XML data with all property elements supplied" do
|
45
|
+
xml_input = <<-XML
|
46
|
+
<PLRequest>
|
47
|
+
<Command>PPGETSIGNATURE</Command>
|
48
|
+
<ClientMAC>the-MAC</ClientMAC>
|
49
|
+
</PLRequest>
|
50
|
+
XML
|
51
|
+
|
52
|
+
actual_instance = described_class.xml_deserialize xml_input
|
53
|
+
|
54
|
+
expect( actual_instance.client_mac ).to eq( 'the-MAC' )
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
it "accepts a canonical visitor" do
|
59
|
+
visitor = Object.new
|
60
|
+
visitor.extend Data::Requests::CanVisit
|
61
|
+
expect{ subject.accept_visitor visitor }.not_to raise_exception
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sends itself to an accepted visitor" do
|
65
|
+
visitor = double :visitor
|
66
|
+
expect( visitor ).to receive( :visit_PinPadGetSignature ).with( subject )
|
67
|
+
subject.accept_visitor visitor
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -30,6 +30,7 @@ module ClientForPoslynx
|
|
30
30
|
subject.transaction_date = 'the-date'
|
31
31
|
subject.transaction_time = 'the-time'
|
32
32
|
subject.input_method = 'the-input-method'
|
33
|
+
subject.signature = 'the-signature'
|
33
34
|
subject.receipt = [ 'Merchant Receipt', '...' ]
|
34
35
|
subject.customer_receipt = [ 'Customer Receipt', '...' ]
|
35
36
|
|
@@ -52,6 +53,7 @@ module ClientForPoslynx
|
|
52
53
|
"<TransactionDate>the-date</TransactionDate>" +
|
53
54
|
"<TransactionTime>the-time</TransactionTime>" +
|
54
55
|
"<InputMethod>the-input-method</InputMethod>" +
|
56
|
+
"<Signature>the-signature</Signature>" +
|
55
57
|
"<Receipt>" +
|
56
58
|
"<Receipt1>Merchant Receipt</Receipt1>" +
|
57
59
|
"<Receipt2>...</Receipt2>" +
|
@@ -98,6 +100,7 @@ module ClientForPoslynx
|
|
98
100
|
<TransactionDate>the-date</TransactionDate>
|
99
101
|
<TransactionTime>the-time</TransactionTime>
|
100
102
|
<InputMethod>the-input-method</InputMethod>
|
103
|
+
<Signature>the-signature</Signature>
|
101
104
|
<Receipt>
|
102
105
|
<Receipt1>Merchant Receipt</Receipt1>
|
103
106
|
<Receipt2>...</Receipt2>
|
@@ -127,6 +130,7 @@ module ClientForPoslynx
|
|
127
130
|
expect( actual_instance.transaction_date ).to eq( 'the-date' )
|
128
131
|
expect( actual_instance.transaction_time ).to eq( 'the-time' )
|
129
132
|
expect( actual_instance.input_method ).to eq( 'the-input-method' )
|
133
|
+
expect( actual_instance.signature ).to eq( 'the-signature' )
|
130
134
|
expect( actual_instance.receipt ).to eq( ['Merchant Receipt', '...'] )
|
131
135
|
expect( actual_instance.customer_receipt ).to eq( ['Customer Receipt', '...'] )
|
132
136
|
end
|
@@ -2,29 +2,27 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module ClientForPoslynx
|
4
4
|
|
5
|
-
describe Data::Responses::
|
5
|
+
describe Data::Responses::PinPadGetSignature do
|
6
6
|
|
7
7
|
it_behaves_like "a data object"
|
8
8
|
|
9
|
-
it "Serializes to a PLResponse XML document for a
|
10
|
-
expected_xml = "<PLResponse><Command>
|
9
|
+
it "Serializes to a PLResponse XML document for a PPGETSIGNATURE response" do
|
10
|
+
expected_xml = "<PLResponse><Command>PPGETSIGNATURE</Command></PLResponse>\n"
|
11
11
|
expect( subject.xml_serialize ).to eq( expected_xml )
|
12
12
|
end
|
13
13
|
|
14
14
|
|
15
15
|
it "Serializes all assigned members to appropriate elements" do
|
16
|
-
subject.result
|
17
|
-
subject.result_text
|
18
|
-
subject.
|
19
|
-
subject.button_response = 'the-response'
|
16
|
+
subject.result = 'the-result'
|
17
|
+
subject.result_text = 'the-text'
|
18
|
+
subject.signature = 'the-signature'
|
20
19
|
|
21
20
|
expected_xml =
|
22
21
|
"<PLResponse>" +
|
23
|
-
"<Command>
|
22
|
+
"<Command>PPGETSIGNATURE</Command>" +
|
24
23
|
"<Result>the-result</Result>" +
|
25
24
|
"<ResultText>the-text</ResultText>" +
|
26
|
-
"<
|
27
|
-
"<Response>the-response</Response>" +
|
25
|
+
"<Signature>the-signature</Signature>" +
|
28
26
|
"</PLResponse>\n"
|
29
27
|
|
30
28
|
expect( subject.xml_serialize ).to eq( expected_xml )
|
@@ -34,7 +32,7 @@ module ClientForPoslynx
|
|
34
32
|
it "parses minimally acceptable XML data" do
|
35
33
|
xml_input = <<-XML
|
36
34
|
<PLResponse>
|
37
|
-
<Command>
|
35
|
+
<Command>PPGETSIGNATURE</Command>
|
38
36
|
</PLResponse>
|
39
37
|
XML
|
40
38
|
|
@@ -46,20 +44,18 @@ module ClientForPoslynx
|
|
46
44
|
it "parses XML data with all property elements supplied" do
|
47
45
|
xml_input = <<-XML
|
48
46
|
<PLResponse>
|
49
|
-
<Command>
|
47
|
+
<Command>PPGETSIGNATURE</Command>
|
50
48
|
<Result>the-result</Result>
|
51
49
|
<ResultText>the-text</ResultText>
|
52
|
-
<
|
53
|
-
<Response>the-response</Response>
|
50
|
+
<Signature>the-signature</Signature>
|
54
51
|
</PLResponse>
|
55
52
|
XML
|
56
53
|
|
57
54
|
actual_instance = described_class.xml_deserialize xml_input
|
58
55
|
|
59
|
-
expect( actual_instance.result
|
60
|
-
expect( actual_instance.result_text
|
61
|
-
expect( actual_instance.
|
62
|
-
expect( actual_instance.button_response ).to eq( 'the-response' )
|
56
|
+
expect( actual_instance.result ).to eq( 'the-result' )
|
57
|
+
expect( actual_instance.result_text ).to eq( 'the-text' )
|
58
|
+
expect( actual_instance.signature ).to eq( 'the-signature' )
|
63
59
|
end
|
64
60
|
|
65
61
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module ClientForPoslynx
|
6
|
+
|
7
|
+
describe SignatureImage::ToSvgConverter do
|
8
|
+
let( :svg_ns ) { 'http://www.w3.org/2000/svg' }
|
9
|
+
let( :signature_image ) { SignatureImage.new }
|
10
|
+
let( :svg_doc_content ) { described_class.convert( signature_image ) }
|
11
|
+
let( :svg_document ) {
|
12
|
+
Nokogiri::XML( svg_doc_content ) do |config|
|
13
|
+
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
|
14
|
+
end
|
15
|
+
}
|
16
|
+
let( :svg_doc_root ) { svg_document.root }
|
17
|
+
|
18
|
+
it "produces SVG document content" do
|
19
|
+
expect( svg_doc_root.name ).to eq( 'svg' )
|
20
|
+
expect( svg_doc_root.namespace.href ).to eq( svg_ns )
|
21
|
+
expect( svg_doc_root['version'] ).to eq( '1.1' )
|
22
|
+
end
|
23
|
+
|
24
|
+
it "specifies physical dimensions in millimeter units" do
|
25
|
+
expected = /^\d+([.]\d*)?mm?$/
|
26
|
+
expect( svg_doc_root['width' ] ).to match( expected )
|
27
|
+
expect( svg_doc_root['height'] ).to match( expected )
|
28
|
+
end
|
29
|
+
|
30
|
+
it "specifies integer logical units for view box start and size" do
|
31
|
+
expect( svg_doc_root['viewBox'] ).to match( /^(\d+\s+){3}\d+$/ )
|
32
|
+
end
|
33
|
+
|
34
|
+
it "treats logical units as fractions of physical dimensions, regardless of resulting pixel aspect ratio" do
|
35
|
+
expect( svg_doc_root['preserveAspectRatio'] ).to eq( 'none' )
|
36
|
+
end
|
37
|
+
|
38
|
+
it "includes a an SVG <path> element" do
|
39
|
+
expect( svg_document.xpath('//ns:svg/ns:path', 'ns' => svg_ns).length ).
|
40
|
+
to eq( 1 )
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "<path> element" do
|
44
|
+
let( :svg_path_element ) {
|
45
|
+
svg_document.at_xpath('//ns:svg/ns:path', 'ns' => svg_ns)
|
46
|
+
}
|
47
|
+
|
48
|
+
it "has a black stroke color" do
|
49
|
+
expect( svg_path_element['stroke'] ).to eq( 'black' )
|
50
|
+
end
|
51
|
+
|
52
|
+
it "is explicitly not filled" do
|
53
|
+
expect( svg_path_element['fill'] ).to eq( 'none' )
|
54
|
+
end
|
55
|
+
|
56
|
+
it "has a stroke width in millimeter units" do
|
57
|
+
expected = /^\d+([.]\d*)?mm?$/
|
58
|
+
expect( svg_path_element['stroke-width'] ).to match( expected )
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "path data" do
|
62
|
+
let( :svg_path_instruction_tokens ) {
|
63
|
+
path_data = svg_path_element['d']
|
64
|
+
path_instructions = path_data.split(/ (?=[Ml])/)
|
65
|
+
path_instructions.map { |pi|
|
66
|
+
instr_type = pi[0..0]
|
67
|
+
instr_values = pi[1..-1].strip.split(/[ ,]+/)
|
68
|
+
[ instr_type ] + instr_values
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
it "creates path data from signature data steps" do
|
73
|
+
signature_image.move 25, 20
|
74
|
+
signature_image.draw 30, 1
|
75
|
+
|
76
|
+
signature_image.move 10, 20
|
77
|
+
signature_image.draw 0, 30
|
78
|
+
signature_image.draw -1, 25
|
79
|
+
|
80
|
+
expect( svg_path_instruction_tokens ).to eq( [
|
81
|
+
%w[ M 25 20 ],
|
82
|
+
%w[ l 30 1 ],
|
83
|
+
|
84
|
+
%w[ M 10 20 ],
|
85
|
+
%w[ l 0 30
|
86
|
+
-1 25 ],
|
87
|
+
] )
|
88
|
+
end
|
89
|
+
|
90
|
+
it "adds zero length line-to after a move without a draw" do
|
91
|
+
signature_image.move 25, 20
|
92
|
+
signature_image.draw 30, 1
|
93
|
+
|
94
|
+
signature_image.move 10, 20
|
95
|
+
|
96
|
+
signature_image.move 90, 30
|
97
|
+
signature_image.draw -1, 25
|
98
|
+
|
99
|
+
expect( svg_path_instruction_tokens ).to eq( [
|
100
|
+
%w[ M 25 20 ],
|
101
|
+
%w[ l 30 1 ],
|
102
|
+
|
103
|
+
%w[ M 10 20 ],
|
104
|
+
%w[ l 0 0 ],
|
105
|
+
|
106
|
+
%w[ M 90 30 ],
|
107
|
+
%w[ l -1 25 ],
|
108
|
+
] )
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module ClientForPoslynx
|
6
|
+
|
7
|
+
describe SignatureImage do
|
8
|
+
|
9
|
+
def build_example_image
|
10
|
+
described_class.new.tap { |si|
|
11
|
+
si.move 10, 120
|
12
|
+
si.draw 0, -30
|
13
|
+
si.draw 0, -30
|
14
|
+
si.draw 0, -30
|
15
|
+
si.draw 0, -20
|
16
|
+
si.draw 30, 30
|
17
|
+
si.draw -25, 25
|
18
|
+
si.draw 0, -2
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
let( :bit_sequence ) {
|
23
|
+
BitSequence.from_bit_digits( bit_digit_sequence )
|
24
|
+
}
|
25
|
+
|
26
|
+
let( :bit_digit_sequence ) {
|
27
|
+
# TODO: Currently assuming that deltas for move are
|
28
|
+
# expressed as sign bit and magnitude, where sign bit
|
29
|
+
# of 1 means negative. Documentation is unclear about
|
30
|
+
# this though except to say that there is a sign bit,
|
31
|
+
# and that values can range from -31 to 31.
|
32
|
+
# Once I can connect to the virtual POSLynx again, I
|
33
|
+
# need to do a test, and find out whether this
|
34
|
+
# assumption is true or not.
|
35
|
+
|
36
|
+
'1' + '0000001010' + '1111000' + # move 10, 120
|
37
|
+
'0' + '000000' + '111110' + # draw 0, -30
|
38
|
+
'0' + '000000' + '111110' + # draw 0, -30
|
39
|
+
'0' + '000000' + '111110' + # draw 0, -30
|
40
|
+
'0' + '000000' + '110100' + # draw 0, -20
|
41
|
+
'0' + '011110' + '011110' + # draw 30, 30
|
42
|
+
'0' + '111001' + '011001' + # draw -25, 25
|
43
|
+
'0' + '000000' + '100010' + # draw 0, -2
|
44
|
+
'000' # remaining bits in last byte
|
45
|
+
}
|
46
|
+
|
47
|
+
it "is unequal to another instance with a different sequence of steps" do
|
48
|
+
subject = build_example_image
|
49
|
+
|
50
|
+
other_sig = described_class.new
|
51
|
+
other_sig.move 10, 120
|
52
|
+
other_sig.draw 0, -29
|
53
|
+
|
54
|
+
expect( subject ).not_to eq( other_sig )
|
55
|
+
end
|
56
|
+
|
57
|
+
it "is equal to another instance with the same sequence of steps" do
|
58
|
+
subject = build_example_image
|
59
|
+
other_sig = build_example_image
|
60
|
+
|
61
|
+
expect( subject ).to eq( other_sig )
|
62
|
+
end
|
63
|
+
|
64
|
+
context "serializing" do
|
65
|
+
subject{ build_example_image }
|
66
|
+
|
67
|
+
it "serializes data in legacy format" do
|
68
|
+
actual_serialized = subject.serialize_legacy
|
69
|
+
actual_bit_sequence = BitSequence.from_uuencoded( actual_serialized )
|
70
|
+
|
71
|
+
expect( actual_bit_sequence ).to eq( bit_sequence )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "deserializing" do
|
76
|
+
subject{ described_class.new(serialized_data) }
|
77
|
+
let( :serialized_data ) {
|
78
|
+
bit_sequence.uuencode
|
79
|
+
}
|
80
|
+
|
81
|
+
it "deserializes data from legacy format" do
|
82
|
+
actual = described_class.deserialize( serialized_data )
|
83
|
+
expected = build_example_image
|
84
|
+
|
85
|
+
expect( actual ).to eq( expected )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/spec/spec_helper.rb
CHANGED