groonga 0.0.6 → 0.0.7

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 (55) hide show
  1. data/AUTHORS +4 -0
  2. data/NEWS.ja.rdoc +10 -0
  3. data/NEWS.rdoc +10 -0
  4. data/README.ja.rdoc +9 -3
  5. data/README.rdoc +10 -4
  6. data/Rakefile +1 -1
  7. data/TUTORIAL.ja.rdoc +3 -6
  8. data/example/bookmark.rb +1 -1
  9. data/example/search/config.ru +52 -28
  10. data/ext/rb-grn-column.c +24 -18
  11. data/ext/rb-grn-context.c +165 -17
  12. data/ext/rb-grn-encoding.c +37 -0
  13. data/ext/rb-grn-expression.c +286 -51
  14. data/ext/rb-grn-object.c +27 -8
  15. data/ext/rb-grn-operation.c +128 -22
  16. data/ext/rb-grn-patricia-trie.c +62 -0
  17. data/ext/rb-grn-snippet.c +7 -17
  18. data/ext/rb-grn-table.c +101 -31
  19. data/ext/rb-grn-utils.c +87 -22
  20. data/ext/rb-grn-variable-size-column.c +1 -1
  21. data/ext/rb-grn.h +27 -4
  22. data/ext/rb-groonga.c +12 -2
  23. data/extconf.rb +2 -1
  24. data/html/index.html +2 -2
  25. data/lib/groonga.rb +1 -0
  26. data/lib/groonga/expression-builder.rb +47 -12
  27. data/lib/groonga/patricia-trie.rb +40 -0
  28. data/lib/groonga/record.rb +17 -13
  29. data/misc/grnop2ruby.rb +49 -0
  30. data/pkg-config.rb +1 -1
  31. data/test-unit/lib/test/unit/assertions.rb +5 -2
  32. data/test-unit/lib/test/unit/autorunner.rb +19 -4
  33. data/test-unit/lib/test/unit/collector/load.rb +3 -1
  34. data/test-unit/lib/test/unit/color-scheme.rb +5 -1
  35. data/test-unit/lib/test/unit/error.rb +7 -5
  36. data/test-unit/lib/test/unit/runner/tap.rb +8 -0
  37. data/test-unit/lib/test/unit/ui/console/testrunner.rb +63 -8
  38. data/test-unit/lib/test/unit/ui/tap/testrunner.rb +92 -0
  39. data/test-unit/test/collector/test-load.rb +1 -5
  40. data/test-unit/test/test-color-scheme.rb +4 -0
  41. data/test/groonga-test-utils.rb +10 -0
  42. data/test/run-test.rb +5 -1
  43. data/test/test-column.rb +58 -0
  44. data/test/test-database.rb +8 -1
  45. data/test/test-expression.rb +48 -6
  46. data/test/test-hash.rb +7 -0
  47. data/test/test-patricia-trie.rb +39 -0
  48. data/test/test-record.rb +2 -2
  49. data/test/test-remote.rb +52 -0
  50. data/test/test-schema.rb +1 -1
  51. data/test/test-table-select-normalize.rb +48 -0
  52. data/test/test-table-select.rb +101 -0
  53. data/test/test-table.rb +0 -9
  54. data/test/test-variable-size-column.rb +28 -0
  55. metadata +16 -5
@@ -240,11 +240,7 @@ EOT
240
240
  [:test, {:name => "test1_2"}]]],
241
241
  @test_case1.to_s)
242
242
 
243
- assert_collect([:suite, {:name => @no_load_sub_test_case5.basename.to_s},
244
- [:suite, {:name => _test_case_name("NoLoadSubTestCase5")},
245
- [:test, {:name => "test5_1"}],
246
- [:test, {:name => "test5_2"}]]],
247
- @no_load_sub_test_case5.to_s)
243
+ assert_collect(nil, @no_load_sub_test_case5.to_s)
248
244
  end
249
245
 
250
246
  def test_nil_pattern
@@ -8,6 +8,10 @@ class TestUnitColorScheme < Test::Unit::TestCase
8
8
  "notification" => color("cyan", :bold => true),
9
9
  "error" => color("yellow", :bold => true) +
10
10
  color("black", :foreground => false),
11
+ "case" => color("white", :bold => true) +
12
+ color("blue", :foreground => false),
13
+ "suite" => color("white", :bold => true) +
14
+ color("green", :foreground => false),
11
15
  },
12
16
  Test::Unit::ColorScheme.default.to_hash)
13
17
  end
@@ -15,6 +15,9 @@
15
15
 
16
16
  require 'fileutils'
17
17
  require 'pathname'
18
+ require 'time'
19
+ require 'json'
20
+ require 'pkg-config'
18
21
 
19
22
  require 'groonga'
20
23
 
@@ -88,4 +91,11 @@ module GroongaTestUtils
88
91
  def teardown_tmp_directory
89
92
  FileUtils.rm_rf(@tmp_dir.to_s)
90
93
  end
94
+
95
+ private
96
+ def assert_equal_select_result(expected, actual)
97
+ assert_equal(expected,
98
+ actual.collect {|record| record.key},
99
+ actual.expression.inspect)
100
+ end
91
101
  end
@@ -43,11 +43,15 @@ ARGV.unshift("--priority-mode")
43
43
 
44
44
  $LOAD_PATH.unshift(ext_dir)
45
45
  $LOAD_PATH.unshift(lib_dir)
46
+ $LOAD_PATH.unshift(base_dir)
46
47
 
47
48
  $LOAD_PATH.unshift(test_dir)
48
49
  require 'groonga-test-utils'
49
50
 
50
- Dir.glob("test/**/test{_,-}*.rb") do |file|
51
+ pkg_config = File.join(base_dir, "vendor", "local", "lib", "pkgconfig")
52
+ PackageConfig.prepend_default_path(pkg_config)
53
+
54
+ Dir.glob("#{base_dir}/test/**/test{_,-}*.rb") do |file|
51
55
  require file.sub(/\.rb$/, '')
52
56
  end
53
57
 
@@ -174,6 +174,64 @@ class ColumnTest < Test::Unit::TestCase
174
174
  assert_equal("title", title.local_name)
175
175
  end
176
176
 
177
+ def test_select
178
+ posts = Groonga::Hash.create(:name => "<posts>", :key_type => "<shorttext>")
179
+ body = posts.define_column("body", "<text>")
180
+
181
+ index = Groonga::PatriciaTrie.create(:name => "<terms>",
182
+ :key_type => "<shorttext>",
183
+ :key_normalize => true)
184
+ index.default_tokenizer = "<token:bigram>"
185
+ body_index = index.define_index_column("body_index", posts,
186
+ :with_position => true,
187
+ :source => body)
188
+
189
+ first_post = posts.add("Hello!", :body => "World")
190
+ hobby = posts.add("My Hobby", :body => "Drive and Eat")
191
+
192
+ result = body.select("drive")
193
+ assert_equal(["Drive and Eat"],
194
+ result.records.collect do |record|
195
+ record["body"]
196
+ end)
197
+ end
198
+
199
+ def test_select_with_block
200
+ posts = Groonga::Hash.create(:name => "<posts>", :key_type => "<shorttext>")
201
+ body = posts.define_column("body", "<text>")
202
+
203
+ index = Groonga::PatriciaTrie.create(:name => "<terms>",
204
+ :key_type => "<shorttext>",
205
+ :key_normalize => true)
206
+ index.default_tokenizer = "<token:bigram>"
207
+ body_index = index.define_index_column("body_index", posts,
208
+ :with_position => true,
209
+ :source => body)
210
+
211
+ first_post = posts.add("Hello!", :body => "World")
212
+ hobby = posts.add("My Hobby", :body => "Drive and Eat")
213
+
214
+ result = body.select do |column|
215
+ column =~ "drive"
216
+ end
217
+ assert_equal(["Drive and Eat"],
218
+ result.records.collect do |record|
219
+ record["body"]
220
+ end)
221
+ end
222
+
223
+ def test_set_time
224
+ posts = Groonga::Hash.create(:name => "<posts>", :key_type => "<shorttext>")
225
+ body = posts.define_column("issued", "<time>")
226
+
227
+ post = posts.add("hello", :issued => 123456)
228
+ assert_equal(Time.at(123456), post[".issued"])
229
+ post = posts.add("groonga", :issued => 1251380635)
230
+ assert_equal(Time.parse("2009-08-27 22:43:55"), post[".issued"])
231
+ post = posts.add("mroonga", :issued => 1251380635.1234567)
232
+ assert_in_delta(Time.at(1251380635.1234567).usec, post[".issued"].usec, 10)
233
+ end
234
+
177
235
  private
178
236
  def assert_content_search(expected_records, term)
179
237
  records = @bookmarks_index_content.search(term).records
@@ -79,19 +79,26 @@ class DatabaseTest < Test::Unit::TestCase
79
79
  "TokenMecab",
80
80
  "TokenTrigram",
81
81
  "TokenUnigram",
82
+ "TokyoGeoPoint",
82
83
  "UInt16",
83
84
  "UInt32",
84
85
  "UInt64",
85
86
  "UInt8",
87
+ "WGS84GeoPoint",
86
88
  "column_create",
87
89
  "column_list",
88
90
  "define_selector",
89
91
  "expr_missing",
90
92
  "load",
93
+ "now",
94
+ "quit",
95
+ "rand",
91
96
  "select",
97
+ "shutdown",
92
98
  "status",
93
99
  "table_create",
94
- "table_list"],
100
+ "table_list",
101
+ "view_add"],
95
102
  database.collect {|object| object.name}.sort)
96
103
  end
97
104
 
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
3
  #
3
4
  # This library is free software; you can redistribute it and/or
@@ -27,9 +28,10 @@ class ExpressionTest < Test::Unit::TestCase
27
28
  expression = Groonga::Expression.new
28
29
  expression.append_constant(morita)
29
30
  expression.append_constant("name")
30
- expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
31
+ expression.append_operation(Groonga::Operation::GET_VALUE, 2)
31
32
  expression.compile
32
- assert_equal("mori daijiro", expression.execute)
33
+ expression.execute
34
+ assert_equal("mori daijiro", context.pop)
33
35
  end
34
36
 
35
37
  def test_get_value_with_variable
@@ -44,12 +46,52 @@ class ExpressionTest < Test::Unit::TestCase
44
46
  variable.value = morita
45
47
  expression.append_object(variable)
46
48
  expression.append_constant("name")
47
- expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
49
+ expression.append_operation(Groonga::Operation::GET_VALUE, 2)
48
50
  expression.compile
49
-
50
- assert_equal("mori daijiro", expression.execute)
51
+ expression.execute
52
+ assert_equal("mori daijiro", context.pop)
51
53
 
52
54
  variable.value = gunyara_kun.id
53
- assert_equal("Tasuku SUENAGA", expression.execute)
55
+ expression.execute
56
+ assert_equal("Tasuku SUENAGA", context.pop)
57
+ end
58
+
59
+ def test_inspect
60
+ expression = Groonga::Expression.new
61
+ expression.append_constant(1)
62
+ expression.append_constant(1)
63
+ expression.append_operation(Groonga::Operation::PLUS, 2)
64
+ expression.compile
65
+
66
+ assert_equal("#<Groonga::Expression noname(){1 1}>", expression.inspect)
67
+ end
68
+
69
+ def test_snippet
70
+ users = Groonga::Array.create(:name => "users")
71
+ name = users.define_column("name", "ShortText")
72
+ terms = Groonga::Hash.create(:name => "terms",
73
+ :key_type => "ShortText",
74
+ :default_tokenizer => "TokenBigram")
75
+ users.define_index_column("user_name", users,
76
+ :source => "users.name",
77
+ :with_position => true)
78
+
79
+ expression = Groonga::Expression.new
80
+ variable = expression.define_variable(:domain => users)
81
+ expression.append_object(variable)
82
+ expression.parse("ラングバ OR Ruby OR groonga", :default_column => name)
83
+ expression.compile
84
+
85
+ snippet = expression.snippet([["[[", "]]"], ["<", ">"]],
86
+ :width => 30)
87
+ assert_equal(["[[ラングバ]]プロジェクト",
88
+ "ン[[groonga]]の機能を<Ruby>か",
89
+ "。[[groonga]]の機能を<Ruby>ら"],
90
+ snippet.execute("ラングバプロジェクトはカラムストア機能も" +
91
+ "備える高速・高機能な全文検索エンジンgroonga" +
92
+ "の機能をRubyから利用するためのライブラリを" +
93
+ "提供するプロジェクトです。groongaの機能を" +
94
+ "Rubyらしい読み書きしやすい構文で利用できる" +
95
+ "ことが利点です。"))
54
96
  end
55
97
  end
@@ -203,4 +203,11 @@ class HashTest < Test::Unit::TestCase
203
203
  users.add("morita")
204
204
  assert_true(users.has_key?("morita"))
205
205
  end
206
+
207
+ def test_big_key
208
+ hash = Groonga::Hash.create(:key_type => "UInt64")
209
+ assert_nothing_raised do
210
+ hash.add(1 << 63)
211
+ end
212
+ end
206
213
  end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
3
  #
3
4
  # This library is free software; you can redistribute it and/or
@@ -88,4 +89,42 @@ class PatriciaTrieTest < Test::Unit::TestCase
88
89
  users.add("morita")
89
90
  assert_true(users.has_key?("morita"))
90
91
  end
92
+
93
+ def test_scan
94
+ Groonga::Context.default_options = {:encoding => "utf-8"}
95
+ words = Groonga::PatriciaTrie.create(:key_type => "ShortText",
96
+ :key_normalize => true)
97
+ words.add("リンク")
98
+ adventure_of_link = words.add('リンクの冒険')
99
+ words.add('冒険')
100
+ gaxtu = words.add('ガッ')
101
+ muteki = words.add('MUTEKI')
102
+ assert_equal([[muteki, "muTEki", 0, 6],
103
+ [adventure_of_link, "リンクの冒険", 7, 18],
104
+ [gaxtu, "ガッ", 42, 6]],
105
+ words.scan('muTEki リンクの冒険 ミリバール ガッ'))
106
+ end
107
+
108
+ def test_tag_keys
109
+ Groonga::Context.default_options = {:encoding => "utf-8"}
110
+ words = Groonga::PatriciaTrie.create(:key_type => "ShortText",
111
+ :key_normalize => true)
112
+ words.add("リンク")
113
+ words.add('リンクの冒険')
114
+ words.add('冒険')
115
+ words.add('㍊')
116
+ words.add('ガッ')
117
+ words.add('MUTEKI')
118
+
119
+ text = 'muTEki リンクの冒険 マッチしない ミリバール ガッ'
120
+ actual = words.tag_keys(text) do |record, word|
121
+ "<#{word}(#{record.key})>"
122
+ end
123
+ assert_equal("<muTEki(muteki)> " +
124
+ "<リンクの冒険(リンクの冒険)> " +
125
+ "マッチしない " +
126
+ "<ミリバール(ミリバール)> " +
127
+ "<ガッ(ガッ)>",
128
+ actual)
129
+ end
91
130
  end
@@ -122,14 +122,14 @@ class RecordTest < Test::Unit::TestCase
122
122
 
123
123
  def test_get_nonexistent_column
124
124
  groonga = @bookmarks.add
125
- assert_raise(Groonga::Error) do
125
+ assert_raise(Groonga::InvalidArgument) do
126
126
  groonga["nonexistent"]
127
127
  end
128
128
  end
129
129
 
130
130
  def test_set_nonexistent_column
131
131
  groonga = @bookmarks.add
132
- assert_raise(Groonga::Error) do
132
+ assert_raise(Groonga::InvalidArgument) do
133
133
  groonga["nonexistent"] = "value"
134
134
  end
135
135
  end
@@ -0,0 +1,52 @@
1
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ class RemoteTest < Test::Unit::TestCase
17
+ include GroongaTestUtils
18
+
19
+ setup :before => :append
20
+ def setup_remote_connection
21
+ @process_id = nil
22
+
23
+ package_config = PKGConfig.package_config("groonga")
24
+ groonga = package_config.variable("groonga")
25
+
26
+ @host = "127.0.0.1"
27
+ @port = 12345
28
+ @remote_database_path = @tmp_dir + "remote-database"
29
+ @process_id = Process.fork do
30
+ exec(groonga,
31
+ "-i", @host,
32
+ "-p", @port.to_s,
33
+ "-s", "-n", @remote_database_path.to_s)
34
+ end
35
+ sleep(1)
36
+ end
37
+
38
+ teardown
39
+ def teardown_remote_connection
40
+ Process.kill(:TERM, @process_id) if @process_id
41
+ end
42
+
43
+ def test_send
44
+ _context = Groonga::Context.new
45
+ _context.connect(:host => @host, :port => @port)
46
+ assert_equal(0, _context.send("status"))
47
+ id, result = _context.receive
48
+ assert_equal(0, id)
49
+ assert_equal(["alloc_count", "starttime", "uptime"],
50
+ JSON.load(result).keys.sort)
51
+ end
52
+ end
@@ -125,7 +125,7 @@ class SchemaTest < Test::Unit::TestCase
125
125
  "id: <#{table.id}>, " +
126
126
  "name: <<posts>>, " +
127
127
  "path: <#{path}>, " +
128
- "domain: <nil>, " +
128
+ "domain: <#{type.inspect}>, " +
129
129
  "range: <#{type.inspect}>, " +
130
130
  "flags: <>, " +
131
131
  "size: <0>>",
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
3
+ # Copyright (C) 2009 SHIDARA Yoji <dara@shidara.net>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ class TableTestSelectNormalize < Test::Unit::TestCase
19
+ include GroongaTestUtils
20
+
21
+ setup :setup_database
22
+
23
+ setup
24
+ def setup_comments
25
+ @comments = Groonga::Array.create(:name => "comments")
26
+ @comments.define_column("content", "Text")
27
+ @comments.define_column("created_at", "Time")
28
+ terms = Groonga::PatriciaTrie.create(:name => "terms",
29
+ :default_tokenizer => "TokenBigram",
30
+ :key_normalize => true)
31
+ terms.define_index_column("comment_content", @comments,
32
+ :with_section => true,
33
+ :source => "comments.content")
34
+ @japanese_comment =
35
+ @comments.add(:content => "うちのボロTVはまだ現役です",
36
+ :created_at => Time.parse("2009-06-09"))
37
+ end
38
+
39
+ def test_select_query_with_japanese
40
+ result = @comments.select("content:%ボロTV")
41
+ assert_equal_select_result([@japanese_comment], result)
42
+ end
43
+
44
+ def test_select_query_only_in_japanese
45
+ result = @comments.select("content:%うちの")
46
+ assert_equal_select_result([@japanese_comment], result)
47
+ end
48
+ end
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License version 2.1 as published by the Free Software Foundation.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+ class TableTestSelect < Test::Unit::TestCase
18
+ include GroongaTestUtils
19
+
20
+ setup :setup_database
21
+
22
+ setup
23
+ def setup_comments
24
+ @comments = Groonga::Array.create(:name => "comments")
25
+ @comments.define_column("content", "Text")
26
+ @comments.define_column("created_at", "Time")
27
+ terms = Groonga::PatriciaTrie.create(:name => "terms",
28
+ :default_tokenizer => "TokenBigram")
29
+ terms.define_index_column("comment_content", @comments,
30
+ :with_section => true,
31
+ :source => "comments.content")
32
+ @comment1 = @comments.add(:content => "Hello Good-bye!",
33
+ :created_at => Time.parse("2009-08-09"))
34
+ @comment2 = @comments.add(:content => "Hello World",
35
+ :created_at => Time.parse("2009-07-09"))
36
+ @comment3 = @comments.add(:content => "test",
37
+ :created_at => Time.parse("2009-06-09"))
38
+ @japanese_comment =
39
+ @comments.add(:content => "うちのボロTVはまだ現役です",
40
+ :created_at => Time.parse("2009-06-09"))
41
+ end
42
+
43
+ def test_select_sub_expression
44
+ result = @comments.select do |record|
45
+ record.match("Hello", "content") &
46
+ (record["created_at"] < Time.parse("2009-08-01"))
47
+ end
48
+ assert_equal_select_result([@comment2], result)
49
+ end
50
+
51
+ def test_select_query
52
+ result = @comments.select("content:%Hello")
53
+ assert_equal_select_result([@comment1, @comment2], result)
54
+ end
55
+
56
+ def test_select_query_with_block
57
+ result = @comments.select("content:%Hello") do |record|
58
+ record["created_at"] < Time.parse("2009-08-01")
59
+ end
60
+ assert_equal_select_result([@comment2], result)
61
+ end
62
+
63
+ def test_select_query_with_block_match
64
+ result = @comments.select("content:%Hello") do |record|
65
+ record.match("World", "content")
66
+ end
67
+ assert_equal_select_result([@comment2], result)
68
+ end
69
+
70
+ def test_select_without_block
71
+ assert_equal_select_result([@comment1, @comment2,
72
+ @comment3, @japanese_comment],
73
+ @comments.select)
74
+ end
75
+
76
+ def test_select_query_japanese
77
+ result = @comments.select("content:%ボロTV")
78
+ assert_equal_select_result([@japanese_comment], result)
79
+ end
80
+
81
+ def test_select_but_query
82
+ result = @comments.select do |record|
83
+ record["content"].match "Hello -World"
84
+ end
85
+ assert_equal_select_result([@comment1], result)
86
+ end
87
+
88
+ def test_select_query_with_three_terms
89
+ result = @comments.select do |record|
90
+ record["content"].match "Say Hello World"
91
+ end
92
+ assert_equal_select_result([], result)
93
+ end
94
+
95
+ def test_select_query_with_brackets
96
+ result = @comments.select do |record|
97
+ record["content"].match "Say (Hello World)"
98
+ end
99
+ assert_equal_select_result([], result)
100
+ end
101
+ end