pelle-ruby-openid 2.1.8

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.
Files changed (197) hide show
  1. data/CHANGELOG +215 -0
  2. data/CHANGES-2.1.0 +36 -0
  3. data/INSTALL +47 -0
  4. data/LICENSE +210 -0
  5. data/NOTICE +2 -0
  6. data/README +82 -0
  7. data/UPGRADE +127 -0
  8. data/VERSION +1 -0
  9. data/examples/README +32 -0
  10. data/examples/active_record_openid_store/README +58 -0
  11. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  12. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  13. data/examples/active_record_openid_store/init.rb +8 -0
  14. data/examples/active_record_openid_store/lib/association.rb +10 -0
  15. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  16. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  17. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  18. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  19. data/examples/discover +49 -0
  20. data/examples/rails_openid/README +153 -0
  21. data/examples/rails_openid/Rakefile +10 -0
  22. data/examples/rails_openid/app/controllers/application.rb +4 -0
  23. data/examples/rails_openid/app/controllers/consumer_controller.rb +122 -0
  24. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  25. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  26. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  27. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  28. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  29. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  30. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  31. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  32. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  33. data/examples/rails_openid/config/boot.rb +19 -0
  34. data/examples/rails_openid/config/database.yml +74 -0
  35. data/examples/rails_openid/config/environment.rb +54 -0
  36. data/examples/rails_openid/config/environments/development.rb +19 -0
  37. data/examples/rails_openid/config/environments/production.rb +19 -0
  38. data/examples/rails_openid/config/environments/test.rb +19 -0
  39. data/examples/rails_openid/config/routes.rb +24 -0
  40. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  41. data/examples/rails_openid/public/.htaccess +40 -0
  42. data/examples/rails_openid/public/404.html +8 -0
  43. data/examples/rails_openid/public/500.html +8 -0
  44. data/examples/rails_openid/public/dispatch.cgi +12 -0
  45. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  46. data/examples/rails_openid/public/dispatch.rb +12 -0
  47. data/examples/rails_openid/public/favicon.ico +0 -0
  48. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  49. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  50. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  51. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  52. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  53. data/examples/rails_openid/public/robots.txt +1 -0
  54. data/examples/rails_openid/script/about +3 -0
  55. data/examples/rails_openid/script/breakpointer +3 -0
  56. data/examples/rails_openid/script/console +3 -0
  57. data/examples/rails_openid/script/destroy +3 -0
  58. data/examples/rails_openid/script/generate +3 -0
  59. data/examples/rails_openid/script/performance/benchmarker +3 -0
  60. data/examples/rails_openid/script/performance/profiler +3 -0
  61. data/examples/rails_openid/script/plugin +3 -0
  62. data/examples/rails_openid/script/process/reaper +3 -0
  63. data/examples/rails_openid/script/process/spawner +3 -0
  64. data/examples/rails_openid/script/process/spinner +3 -0
  65. data/examples/rails_openid/script/runner +3 -0
  66. data/examples/rails_openid/script/server +3 -0
  67. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  68. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  69. data/examples/rails_openid/test/test_helper.rb +28 -0
  70. data/lib/hmac/hmac.rb +112 -0
  71. data/lib/hmac/sha1.rb +11 -0
  72. data/lib/hmac/sha2.rb +25 -0
  73. data/lib/openid/association.rb +249 -0
  74. data/lib/openid/consumer/associationmanager.rb +344 -0
  75. data/lib/openid/consumer/checkid_request.rb +186 -0
  76. data/lib/openid/consumer/discovery.rb +498 -0
  77. data/lib/openid/consumer/discovery_manager.rb +123 -0
  78. data/lib/openid/consumer/html_parse.rb +134 -0
  79. data/lib/openid/consumer/idres.rb +523 -0
  80. data/lib/openid/consumer/responses.rb +148 -0
  81. data/lib/openid/consumer.rb +395 -0
  82. data/lib/openid/cryptutil.rb +97 -0
  83. data/lib/openid/dh.rb +89 -0
  84. data/lib/openid/extension.rb +39 -0
  85. data/lib/openid/extensions/ax.rb +516 -0
  86. data/lib/openid/extensions/oauth.rb +91 -0
  87. data/lib/openid/extensions/pape.rb +179 -0
  88. data/lib/openid/extensions/sreg.rb +277 -0
  89. data/lib/openid/extras.rb +11 -0
  90. data/lib/openid/fetchers.rb +238 -0
  91. data/lib/openid/kvform.rb +136 -0
  92. data/lib/openid/kvpost.rb +58 -0
  93. data/lib/openid/message.rb +553 -0
  94. data/lib/openid/protocolerror.rb +8 -0
  95. data/lib/openid/server.rb +1544 -0
  96. data/lib/openid/store/filesystem.rb +271 -0
  97. data/lib/openid/store/interface.rb +75 -0
  98. data/lib/openid/store/memcache.rb +107 -0
  99. data/lib/openid/store/memory.rb +84 -0
  100. data/lib/openid/store/nonce.rb +68 -0
  101. data/lib/openid/trustroot.rb +349 -0
  102. data/lib/openid/urinorm.rb +75 -0
  103. data/lib/openid/util.rb +110 -0
  104. data/lib/openid/yadis/accept.rb +148 -0
  105. data/lib/openid/yadis/constants.rb +21 -0
  106. data/lib/openid/yadis/discovery.rb +153 -0
  107. data/lib/openid/yadis/filters.rb +205 -0
  108. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  109. data/lib/openid/yadis/parsehtml.rb +45 -0
  110. data/lib/openid/yadis/services.rb +42 -0
  111. data/lib/openid/yadis/xrds.rb +155 -0
  112. data/lib/openid/yadis/xri.rb +90 -0
  113. data/lib/openid/yadis/xrires.rb +106 -0
  114. data/lib/openid.rb +20 -0
  115. data/setup.rb +1551 -0
  116. data/test/data/accept.txt +124 -0
  117. data/test/data/dh.txt +29 -0
  118. data/test/data/example-xrds.xml +14 -0
  119. data/test/data/linkparse.txt +587 -0
  120. data/test/data/n2b64 +650 -0
  121. data/test/data/test1-discover.txt +137 -0
  122. data/test/data/test1-parsehtml.txt +152 -0
  123. data/test/data/test_discover/malformed_meta_tag.html +19 -0
  124. data/test/data/test_discover/openid.html +11 -0
  125. data/test/data/test_discover/openid2.html +11 -0
  126. data/test/data/test_discover/openid2_xrds.xml +12 -0
  127. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  128. data/test/data/test_discover/openid_1_and_2.html +11 -0
  129. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  130. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  131. data/test/data/test_discover/openid_and_yadis.html +12 -0
  132. data/test/data/test_discover/openid_no_delegate.html +10 -0
  133. data/test/data/test_discover/yadis_0entries.xml +12 -0
  134. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  135. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  136. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  137. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  138. data/test/data/test_discover/yadis_idp.xml +12 -0
  139. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  140. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  141. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  142. data/test/data/test_xrds/README +12 -0
  143. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  144. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  145. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  146. data/test/data/test_xrds/no-xrd.xml +7 -0
  147. data/test/data/test_xrds/not-xrds.xml +2 -0
  148. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  149. data/test/data/test_xrds/ref.xrds +109 -0
  150. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  151. data/test/data/test_xrds/spoof1.xrds +25 -0
  152. data/test/data/test_xrds/spoof2.xrds +25 -0
  153. data/test/data/test_xrds/spoof3.xrds +37 -0
  154. data/test/data/test_xrds/status222.xrds +9 -0
  155. data/test/data/test_xrds/subsegments.xrds +58 -0
  156. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  157. data/test/data/trustroot.txt +153 -0
  158. data/test/data/urinorm.txt +79 -0
  159. data/test/discoverdata.rb +131 -0
  160. data/test/test_accept.rb +170 -0
  161. data/test/test_association.rb +266 -0
  162. data/test/test_associationmanager.rb +917 -0
  163. data/test/test_ax.rb +648 -0
  164. data/test/test_checkid_request.rb +294 -0
  165. data/test/test_consumer.rb +257 -0
  166. data/test/test_cryptutil.rb +119 -0
  167. data/test/test_dh.rb +86 -0
  168. data/test/test_discover.rb +838 -0
  169. data/test/test_discovery_manager.rb +262 -0
  170. data/test/test_extension.rb +46 -0
  171. data/test/test_extras.rb +35 -0
  172. data/test/test_fetchers.rb +538 -0
  173. data/test/test_filters.rb +270 -0
  174. data/test/test_idres.rb +963 -0
  175. data/test/test_kvform.rb +165 -0
  176. data/test/test_kvpost.rb +65 -0
  177. data/test/test_linkparse.rb +101 -0
  178. data/test/test_message.rb +1116 -0
  179. data/test/test_nonce.rb +89 -0
  180. data/test/test_oauth.rb +175 -0
  181. data/test/test_openid_yadis.rb +178 -0
  182. data/test/test_pape.rb +247 -0
  183. data/test/test_parsehtml.rb +80 -0
  184. data/test/test_responses.rb +63 -0
  185. data/test/test_server.rb +2457 -0
  186. data/test/test_sreg.rb +479 -0
  187. data/test/test_stores.rb +298 -0
  188. data/test/test_trustroot.rb +113 -0
  189. data/test/test_urinorm.rb +35 -0
  190. data/test/test_util.rb +145 -0
  191. data/test/test_xrds.rb +169 -0
  192. data/test/test_xri.rb +48 -0
  193. data/test/test_xrires.rb +63 -0
  194. data/test/test_yadis_discovery.rb +220 -0
  195. data/test/testutil.rb +127 -0
  196. data/test/util.rb +53 -0
  197. metadata +316 -0
@@ -0,0 +1,262 @@
1
+
2
+ require 'test/unit'
3
+ require 'openid/consumer/discovery_manager'
4
+ require 'openid/extras'
5
+
6
+ require 'testutil'
7
+
8
+ module OpenID
9
+ class TestDiscoveredServices < Test::Unit::TestCase
10
+ def setup
11
+ @starting_url = "http://starting.url.com/"
12
+ @yadis_url = "http://starting.url.com/xrds"
13
+ @services = ["bogus", "not_a_service"]
14
+
15
+ @disco_services = Consumer::DiscoveredServices.new(@starting_url,
16
+ @yadis_url,
17
+ @services.dup)
18
+ end
19
+
20
+ def test_next
21
+ assert_equal(@disco_services.next, @services[0])
22
+ assert_equal(@disco_services.current, @services[0])
23
+
24
+ assert_equal(@disco_services.next, @services[1])
25
+ assert_equal(@disco_services.current, @services[1])
26
+
27
+ assert_equal(@disco_services.next, nil)
28
+ assert_equal(@disco_services.current, nil)
29
+ end
30
+
31
+ def test_for_url
32
+ assert(@disco_services.for_url?(@starting_url))
33
+ assert(@disco_services.for_url?(@yadis_url))
34
+
35
+ assert(!@disco_services.for_url?(nil))
36
+ assert(!@disco_services.for_url?("invalid"))
37
+ end
38
+
39
+ def test_started
40
+ assert(!@disco_services.started?)
41
+ @disco_services.next
42
+ assert(@disco_services.started?)
43
+ @disco_services.next
44
+ assert(@disco_services.started?)
45
+ @disco_services.next
46
+ assert(!@disco_services.started?)
47
+ end
48
+
49
+ def test_empty
50
+ assert(Consumer::DiscoveredServices.new(nil, nil, []).empty?)
51
+
52
+ assert(!@disco_services.empty?)
53
+
54
+ @disco_services.next
55
+ @disco_services.next
56
+
57
+ assert(@disco_services.started?)
58
+ end
59
+ end
60
+
61
+ # I need to be able to test the protected methods; this lets me do
62
+ # that.
63
+ class PassthroughDiscoveryManager < Consumer::DiscoveryManager
64
+ def method_missing(m, *args)
65
+ method(m).call(*args)
66
+ end
67
+ end
68
+
69
+ class TestDiscoveryManager < Test::Unit::TestCase
70
+ def setup
71
+ @session = {}
72
+ @url = "http://unittest.com/"
73
+ @key_suffix = "testing"
74
+ @yadis_url = "http://unittest.com/xrds"
75
+ @manager = PassthroughDiscoveryManager.new(@session, @url, @key_suffix)
76
+ @key = @manager.session_key
77
+ end
78
+
79
+ def test_construct
80
+ # Make sure the default session key suffix is not nil.
81
+ m = Consumer::DiscoveryManager.new(nil, nil)
82
+ assert(!m.instance_variable_get("@session_key_suffix").nil?)
83
+
84
+ m = Consumer::DiscoveryManager.new(nil, nil, "override")
85
+ assert_equal(m.instance_variable_get("@session_key_suffix"), "override")
86
+ end
87
+
88
+ def test_get_next_service
89
+ assert_equal(@session[@key], nil)
90
+
91
+ next_service = @manager.get_next_service {
92
+ [@yadis_url, ["one", "two", "three"]]
93
+ }
94
+
95
+ disco = @session[@key]
96
+ assert_equal(disco.current, "one")
97
+ assert_equal(next_service, "one")
98
+ assert(disco.for_url?(@url))
99
+ assert(disco.for_url?(@yadis_url))
100
+
101
+ # The first two calls to get_next_service should return the
102
+ # services in @disco.
103
+ assert_equal(@manager.get_next_service, "two")
104
+ assert_equal(@manager.get_next_service, "three")
105
+ assert_equal(@session[@key], disco)
106
+
107
+ # The manager is exhausted and should be deleted and a new one
108
+ # should be created.
109
+ @manager.get_next_service {
110
+ [@yadis_url, ["four"]]
111
+ }
112
+
113
+ disco2 = @session[@key]
114
+ assert_equal(disco2.current, "four")
115
+
116
+ # create_manager may return a nil manager, in which case the
117
+ # next service should be nil.
118
+ @manager.extend(OpenID::InstanceDefExtension)
119
+ @manager.instance_def(:create_manager) do |yadis_url, services|
120
+ nil
121
+ end
122
+
123
+ result = @manager.get_next_service { |url|
124
+ ["unused", []]
125
+ }
126
+
127
+ assert_equal(result, nil)
128
+ end
129
+
130
+ def test_cleanup
131
+ # With no preexisting manager, cleanup() returns nil.
132
+ assert_equal(@manager.cleanup, nil)
133
+
134
+ # With a manager, it returns the manager's current service.
135
+ disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["one", "two"])
136
+
137
+ @session[@key] = disco
138
+ assert_equal(@manager.cleanup, nil)
139
+ assert_equal(@session[@key], nil)
140
+
141
+ @session[@key] = disco
142
+ disco.next
143
+ assert_equal(@manager.cleanup, "one")
144
+ assert_equal(@session[@key], nil)
145
+
146
+ # The force parameter should be passed through to get_manager
147
+ # and destroy_manager.
148
+ force_value = "yo"
149
+ testcase = self
150
+
151
+ m = Consumer::DiscoveredServices.new(nil, nil, ["inner"])
152
+ m.next
153
+
154
+ @manager.extend(OpenID::InstanceDefExtension)
155
+ @manager.instance_def(:get_manager) do |force|
156
+ testcase.assert_equal(force, force_value)
157
+ m
158
+ end
159
+
160
+ @manager.instance_def(:destroy_manager) do |force|
161
+ testcase.assert_equal(force, force_value)
162
+ end
163
+
164
+ assert_equal("inner", @manager.cleanup(force_value))
165
+ end
166
+
167
+ def test_get_manager
168
+ # get_manager should always return the loaded manager when
169
+ # forced.
170
+ @session[@key] = "bogus"
171
+ assert_equal("bogus", @manager.get_manager(true))
172
+
173
+ # When not forced, only managers for @url should be returned.
174
+ disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["one"])
175
+ @session[@key] = disco
176
+ assert_equal(@manager.get_manager, disco)
177
+
178
+ # Try to get_manager for a manger that doesn't manage @url:
179
+ disco2 = Consumer::DiscoveredServices.new("http://not.this.url.com/",
180
+ "http://other.yadis.url/", ["one"])
181
+ @session[@key] = disco2
182
+ assert_equal(@manager.get_manager, nil)
183
+ assert_equal(@manager.get_manager(true), disco2)
184
+ end
185
+
186
+ def test_create_manager
187
+ assert(@session[@key].nil?)
188
+
189
+ services = ["created", "manager"]
190
+ returned_disco = @manager.create_manager(@yadis_url, services)
191
+
192
+ stored_disco = @session[@key]
193
+ assert(stored_disco.for_url?(@yadis_url))
194
+ assert_equal(stored_disco.next, "created")
195
+
196
+ assert_equal(stored_disco, returned_disco)
197
+
198
+ # Calling create_manager with a preexisting manager should
199
+ # result in StandardError.
200
+ assert_raise(StandardError) {
201
+ @manager.create_manager(@yadis_url, services)
202
+ }
203
+
204
+ # create_manager should do nothing (and return nil) if given no
205
+ # services.
206
+ @session[@key] = nil
207
+ result = @manager.create_manager(@yadis_url, [])
208
+ assert(result.nil?)
209
+ assert(@session[@key].nil?)
210
+ end
211
+
212
+ class DestroyCalledException < StandardError; end
213
+
214
+ def test_destroy_manager
215
+ # destroy_manager should remove the manager from the session,
216
+ # forcibly if necessary.
217
+ valid_disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["serv"])
218
+ invalid_disco = Consumer::DiscoveredServices.new("http://not.mine.com/",
219
+ "http://different.url.com/",
220
+ ["serv"])
221
+
222
+ @session[@key] = valid_disco
223
+ @manager.destroy_manager
224
+ assert(@session[@key].nil?)
225
+
226
+ @session[@key] = invalid_disco
227
+ @manager.destroy_manager
228
+ assert_equal(@session[@key], invalid_disco)
229
+
230
+ # Force destruction of manager, no matter which URLs it's for.
231
+ @manager.destroy_manager(true)
232
+ assert(@session[@key].nil?)
233
+ end
234
+
235
+ def test_session_key
236
+ assert(@manager.session_key.ends_with?(
237
+ @manager.instance_variable_get("@session_key_suffix")))
238
+ end
239
+
240
+ def test_store
241
+ thing = "opaque"
242
+ assert(@session[@key].nil?)
243
+ @manager.store(thing)
244
+ assert_equal(@session[@key], thing)
245
+ end
246
+
247
+ def test_load
248
+ thing = "opaque"
249
+ @session[@key] = thing
250
+ assert_equal(@manager.load, thing)
251
+ end
252
+
253
+ def test_destroy!
254
+ thing = "opaque"
255
+ @manager.store(thing)
256
+ assert_equal(@manager.load, thing)
257
+ @manager.destroy!
258
+ assert(@session[@key].nil?)
259
+ assert(@manager.load.nil?)
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,46 @@
1
+ require 'openid/extension'
2
+ require 'openid/message'
3
+ require 'test/unit'
4
+
5
+ module OpenID
6
+ class DummyExtension < OpenID::Extension
7
+ TEST_URI = 'http://an.extension'
8
+ TEST_ALIAS = 'dummy'
9
+ def initialize
10
+ @ns_uri = TEST_URI
11
+ @ns_alias = TEST_ALIAS
12
+ end
13
+
14
+ def get_extension_args
15
+ return {}
16
+ end
17
+ end
18
+
19
+ class ToMessageTest < Test::Unit::TestCase
20
+ def test_OpenID1
21
+ oid1_msg = Message.new(OPENID1_NS)
22
+ ext = DummyExtension.new
23
+ ext.to_message(oid1_msg)
24
+ namespaces = oid1_msg.namespaces
25
+ assert(namespaces.implicit?(DummyExtension::TEST_URI))
26
+ assert_equal(
27
+ DummyExtension::TEST_URI,
28
+ namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS))
29
+ assert_equal(DummyExtension::TEST_ALIAS,
30
+ namespaces.get_alias(DummyExtension::TEST_URI))
31
+ end
32
+
33
+ def test_OpenID2
34
+ oid2_msg = Message.new(OPENID2_NS)
35
+ ext = DummyExtension.new
36
+ ext.to_message(oid2_msg)
37
+ namespaces = oid2_msg.namespaces
38
+ assert(!namespaces.implicit?(DummyExtension::TEST_URI))
39
+ assert_equal(
40
+ DummyExtension::TEST_URI,
41
+ namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS))
42
+ assert_equal(DummyExtension::TEST_ALIAS,
43
+ namespaces.get_alias(DummyExtension::TEST_URI))
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ require 'test/unit'
2
+ require 'openid/extras'
3
+
4
+ class StartsWithTestCase < Test::Unit::TestCase
5
+ def test_starts_with
6
+ [["anything", ""],
7
+ ["something else", ""],
8
+ ["", ""],
9
+ ["foos", "foo"],
10
+ ].each{|str,target| assert(str.starts_with?(target))}
11
+ end
12
+
13
+ def test_not_starts_with
14
+ [["x", "y"],
15
+ ["foos", "ball"],
16
+ ["xx", "xy"],
17
+ ].each{|str,target| assert(!(str.starts_with? target)) }
18
+ end
19
+
20
+ def test_ends_with
21
+ [["anything", ""],
22
+ ["something else", " else"],
23
+ ["", ""],
24
+ ["foos", "oos"],
25
+ ].each{|str,target| assert(str.ends_with?(target))}
26
+ end
27
+
28
+ def test_not_ends_with
29
+ [["x", "y"],
30
+ ["foos", "ball"],
31
+ ["xx", "xy"],
32
+ ["foosball", "foosbal"],
33
+ ].each{|str,target| assert(!(str.ends_with? target)) }
34
+ end
35
+ end