client_for_poslynx 0.2.4 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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