groonga 0.0.7 → 0.9.0
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.
- data/NEWS.ja.rdoc +56 -0
- data/NEWS.rdoc +58 -0
- data/Rakefile +2 -3
- data/benchmark/read-write-many-small-items.rb +16 -32
- data/benchmark/write-many-small-items.rb +14 -28
- data/example/bookmark.rb +19 -17
- data/example/index-html.rb +11 -1
- data/example/search/config.ru +14 -9
- data/ext/rb-grn-array.c +6 -6
- data/ext/rb-grn-column.c +348 -18
- data/ext/rb-grn-context.c +8 -4
- data/ext/rb-grn-database.c +6 -7
- data/ext/rb-grn-exception.c +101 -5
- data/ext/rb-grn-expression.c +206 -23
- data/ext/rb-grn-fix-size-column.c +6 -39
- data/ext/rb-grn-hash.c +24 -24
- data/ext/rb-grn-index-column.c +74 -19
- data/ext/rb-grn-logger.c +48 -0
- data/ext/rb-grn-object.c +281 -67
- data/ext/rb-grn-operation.c +1 -1
- data/ext/rb-grn-patricia-trie-cursor.c +10 -1
- data/ext/rb-grn-patricia-trie.c +268 -7
- data/ext/rb-grn-query.c +52 -1
- data/ext/rb-grn-record.c +8 -2
- data/ext/rb-grn-snippet.c +63 -1
- data/ext/rb-grn-table-cursor-key-support.c +15 -1
- data/ext/rb-grn-table-cursor.c +57 -0
- data/ext/rb-grn-table-key-support.c +382 -46
- data/ext/rb-grn-table.c +729 -192
- data/ext/rb-grn-type.c +63 -12
- data/ext/rb-grn-utils.c +156 -158
- data/ext/rb-grn-variable.c +18 -0
- data/ext/rb-grn.h +85 -21
- data/ext/rb-groonga.c +13 -3
- data/extconf.rb +19 -4
- data/html/developer.html +1 -1
- data/html/header.html.erb +1 -1
- data/html/index.html +4 -4
- data/lib/groonga.rb +10 -0
- data/lib/groonga/expression-builder.rb +81 -42
- data/lib/groonga/patricia-trie.rb +13 -0
- data/lib/groonga/record.rb +158 -13
- data/lib/groonga/schema.rb +339 -33
- data/pkg-config.rb +6 -1
- data/test-unit/lib/test/unit.rb +23 -42
- data/test-unit/lib/test/unit/assertionfailederror.rb +11 -0
- data/test-unit/lib/test/unit/assertions.rb +87 -9
- data/test-unit/lib/test/unit/autorunner.rb +20 -11
- data/test-unit/lib/test/unit/collector.rb +1 -8
- data/test-unit/lib/test/unit/collector/load.rb +2 -3
- data/test-unit/lib/test/unit/color-scheme.rb +13 -1
- data/test-unit/lib/test/unit/diff.rb +223 -37
- data/test-unit/lib/test/unit/error.rb +4 -0
- data/test-unit/lib/test/unit/failure.rb +31 -5
- data/test-unit/lib/test/unit/notification.rb +8 -4
- data/test-unit/lib/test/unit/omission.rb +51 -3
- data/test-unit/lib/test/unit/pending.rb +4 -0
- data/test-unit/lib/test/unit/testcase.rb +55 -4
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +190 -4
- data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +14 -0
- data/test-unit/lib/test/unit/ui/testrunner.rb +8 -0
- data/test-unit/lib/test/unit/version.rb +1 -1
- data/test-unit/sample/{tc_adder.rb → test_adder.rb} +3 -1
- data/test-unit/sample/{tc_subtracter.rb → test_subtracter.rb} +3 -1
- data/test-unit/sample/test_user.rb +1 -0
- data/test-unit/test/collector/test-descendant.rb +2 -4
- data/test-unit/test/collector/test_objectspace.rb +7 -5
- data/test-unit/test/run-test.rb +2 -0
- data/test-unit/test/test-color-scheme.rb +7 -0
- data/test-unit/test/test-diff.rb +48 -7
- data/test-unit/test/test-omission.rb +1 -1
- data/test-unit/test/test-testcase.rb +47 -0
- data/test-unit/test/test_assertions.rb +79 -10
- data/test/groonga-test-utils.rb +6 -1
- data/test/test-array.rb +29 -14
- data/test/test-column.rb +107 -55
- data/test/test-context.rb +5 -0
- data/test/test-database.rb +2 -37
- data/test/test-exception.rb +9 -1
- data/test/test-expression-builder.rb +23 -5
- data/test/test-expression.rb +44 -8
- data/test/test-fix-size-column.rb +16 -5
- data/test/test-gqtp.rb +70 -0
- data/test/test-hash.rb +142 -43
- data/test/test-index-column.rb +9 -9
- data/test/test-patricia-trie.rb +79 -20
- data/test/test-procedure.rb +4 -2
- data/test/test-record.rb +32 -20
- data/test/test-remote.rb +3 -2
- data/test/test-schema.rb +226 -92
- data/test/test-table-cursor.rb +103 -1
- data/test/test-table-offset-and-limit.rb +102 -0
- data/test/test-table-select-normalize.rb +4 -4
- data/test/test-table-select.rb +52 -8
- data/test/test-table.rb +235 -116
- data/test/test-type.rb +2 -2
- data/test/test-variable-size-column.rb +21 -5
- data/test/test-vector-column.rb +76 -0
- data/{TUTORIAL.ja.rdoc → text/TUTORIAL.ja.rdoc} +52 -52
- data/text/expression.rdoc +284 -0
- metadata +11 -7
- data/test-unit/sample/ts_examples.rb +0 -7
data/pkg-config.rb
CHANGED
|
@@ -219,7 +219,12 @@ class PackageConfig
|
|
|
219
219
|
end
|
|
220
220
|
extern "const char *dln_find_exe(const char *, const char *)"
|
|
221
221
|
end
|
|
222
|
-
dln.dln_find_exe(pkg_config.to_s,
|
|
222
|
+
path = dln.dln_find_exe(pkg_config.to_s, nil)
|
|
223
|
+
if path.size.zero?
|
|
224
|
+
nil
|
|
225
|
+
else
|
|
226
|
+
Pathname(path.to_s)
|
|
227
|
+
end
|
|
223
228
|
end
|
|
224
229
|
|
|
225
230
|
def guess_default_path
|
data/test-unit/lib/test/unit.rb
CHANGED
|
@@ -179,7 +179,7 @@ module Test # :nodoc:
|
|
|
179
179
|
#
|
|
180
180
|
# require 'test/unit'
|
|
181
181
|
#
|
|
182
|
-
# class
|
|
182
|
+
# class MyTest < Test::Unit::TestCase
|
|
183
183
|
# # def setup
|
|
184
184
|
# # end
|
|
185
185
|
#
|
|
@@ -194,21 +194,17 @@ module Test # :nodoc:
|
|
|
194
194
|
#
|
|
195
195
|
# == Test Runners
|
|
196
196
|
#
|
|
197
|
-
# So, now you have this great test class, but you still
|
|
198
|
-
# run it and view any failures that occur
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
# runner is automatically invoked for you if you require
|
|
202
|
-
# and simply run the file. To use another
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
# Test::Unit::TestSuite. This can be as simple as passing in your
|
|
206
|
-
# TestCase class (which has a class suite method). It might look
|
|
207
|
-
# something like this:
|
|
208
|
-
#
|
|
209
|
-
# require 'test/unit/ui/console/testrunner'
|
|
210
|
-
# Test::Unit::UI::Console::TestRunner.run(TC_MyTest)
|
|
197
|
+
# So, now you have this great test class, but you still
|
|
198
|
+
# need a way to run it and view any failures that occur
|
|
199
|
+
# during the run. There are some test runner; console test
|
|
200
|
+
# runner, GTK+ test runner and so on. The console test
|
|
201
|
+
# runner is automatically invoked for you if you require
|
|
202
|
+
# 'test/unit' and simply run the file. To use another
|
|
203
|
+
# runner simply set default test runner ID to
|
|
204
|
+
# Test::Unit::AutoRunner:
|
|
211
205
|
#
|
|
206
|
+
# require 'test/unit'
|
|
207
|
+
# Test::Unit::AutoRunner.default_runner = "gtk2"
|
|
212
208
|
#
|
|
213
209
|
# == Test Suite
|
|
214
210
|
#
|
|
@@ -220,33 +216,17 @@ module Test # :nodoc:
|
|
|
220
216
|
# in response to a suite method. The TestSuite can, in turn, contain
|
|
221
217
|
# other TestSuites or individual tests (typically created by a
|
|
222
218
|
# TestCase). In other words, you can easily wrap up a group of
|
|
223
|
-
# TestCases and TestSuites
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
# class TS_MyTests
|
|
231
|
-
# def self.suite
|
|
232
|
-
# suite = Test::Unit::TestSuite.new
|
|
233
|
-
# suite << TC_MyFirstTests.suite
|
|
234
|
-
# suite << TC_MoreTestsByMe.suite
|
|
235
|
-
# suite << TS_AnotherSetOfTests.suite
|
|
236
|
-
# return suite
|
|
237
|
-
# end
|
|
238
|
-
# end
|
|
239
|
-
# Test::Unit::UI::Console::TestRunner.run(TS_MyTests)
|
|
240
|
-
#
|
|
241
|
-
# Now, this is a bit cumbersome, so Test::Unit does a little bit more
|
|
242
|
-
# for you, by wrapping these up automatically when you require
|
|
243
|
-
# 'test/unit'. What does this mean? It means you could write the above
|
|
244
|
-
# test case like this instead:
|
|
219
|
+
# TestCases and TestSuites.
|
|
220
|
+
#
|
|
221
|
+
# Test::Unit does a little bit more for you, by wrapping
|
|
222
|
+
# these up automatically when you require
|
|
223
|
+
# 'test/unit'. What does this mean? It means you could
|
|
224
|
+
# write the above test case like this instead:
|
|
245
225
|
#
|
|
246
226
|
# require 'test/unit'
|
|
247
|
-
# require '
|
|
248
|
-
# require '
|
|
249
|
-
# require '
|
|
227
|
+
# require 'test_myfirsttests'
|
|
228
|
+
# require 'test_moretestsbyme'
|
|
229
|
+
# require 'test_anothersetoftests'
|
|
250
230
|
#
|
|
251
231
|
# Test::Unit is smart enough to find all the test cases existing in
|
|
252
232
|
# the ObjectSpace and wrap them up into a suite for you. It then runs
|
|
@@ -323,12 +303,13 @@ module Test # :nodoc:
|
|
|
323
303
|
#
|
|
324
304
|
|
|
325
305
|
module Unit
|
|
326
|
-
#
|
|
306
|
+
# Set true when Test::Unit has run. If set to true Test::Unit
|
|
307
|
+
# will not automatically run at exit.
|
|
327
308
|
def self.run=(flag)
|
|
328
309
|
@run = flag
|
|
329
310
|
end
|
|
330
311
|
|
|
331
|
-
#
|
|
312
|
+
# Already tests have run?
|
|
332
313
|
def self.run?
|
|
333
314
|
@run ||= false
|
|
334
315
|
end
|
|
@@ -9,6 +9,17 @@ module Test
|
|
|
9
9
|
|
|
10
10
|
# Thrown by Test::Unit::Assertions when an assertion fails.
|
|
11
11
|
class AssertionFailedError < StandardError
|
|
12
|
+
attr_accessor :expected, :actual, :user_message
|
|
13
|
+
attr_accessor :inspected_expected, :inspected_actual
|
|
14
|
+
def initialize(message=nil, options=nil)
|
|
15
|
+
options ||= {}
|
|
16
|
+
@expected = options[:expected]
|
|
17
|
+
@actual = options[:actual]
|
|
18
|
+
@inspected_expected = options[:inspected_expected]
|
|
19
|
+
@inspected_actual = options[:inspected_actual]
|
|
20
|
+
@user_message = options[:user_message]
|
|
21
|
+
super(message)
|
|
22
|
+
end
|
|
12
23
|
end
|
|
13
24
|
end
|
|
14
25
|
end
|
|
@@ -84,7 +84,16 @@ module Test
|
|
|
84
84
|
<?> expected but was
|
|
85
85
|
<?>.?
|
|
86
86
|
EOT
|
|
87
|
-
|
|
87
|
+
begin
|
|
88
|
+
assert_block(full_message) { expected == actual }
|
|
89
|
+
rescue AssertionFailedError => failure
|
|
90
|
+
failure.expected = expected
|
|
91
|
+
failure.actual = actual
|
|
92
|
+
failure.inspected_expected = AssertionMessage.convert(expected)
|
|
93
|
+
failure.inspected_actual = AssertionMessage.convert(actual)
|
|
94
|
+
failure.user_message = message
|
|
95
|
+
raise
|
|
96
|
+
end
|
|
88
97
|
end
|
|
89
98
|
|
|
90
99
|
##
|
|
@@ -789,6 +798,54 @@ EOT
|
|
|
789
798
|
end
|
|
790
799
|
end
|
|
791
800
|
|
|
801
|
+
##
|
|
802
|
+
# Passes if +object+#+alias_name+ is an alias method of
|
|
803
|
+
# +object+#+original_name+.
|
|
804
|
+
#
|
|
805
|
+
# Example:
|
|
806
|
+
# assert_alias_method([], :length, :size) # -> pass
|
|
807
|
+
# assert_alias_method([], :size, :length) # -> pass
|
|
808
|
+
# assert_alias_method([], :each, :size) # -> fail
|
|
809
|
+
def assert_alias_method(object, alias_name, original_name, message=nil)
|
|
810
|
+
_wrap_assertion do
|
|
811
|
+
find_method_failure_message = Proc.new do |method_name|
|
|
812
|
+
build_message(message,
|
|
813
|
+
"<?>.? doesn't exist\n" +
|
|
814
|
+
"(Class: <?>)",
|
|
815
|
+
object,
|
|
816
|
+
AssertionMessage.literal(method_name),
|
|
817
|
+
object.class)
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
alias_method = original_method = nil
|
|
821
|
+
assert_block(find_method_failure_message.call(alias_name)) do
|
|
822
|
+
begin
|
|
823
|
+
alias_method = object.method(alias_name)
|
|
824
|
+
true
|
|
825
|
+
rescue NameError
|
|
826
|
+
false
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
assert_block(find_method_failure_message.call(original_name)) do
|
|
830
|
+
begin
|
|
831
|
+
original_method = object.method(original_name)
|
|
832
|
+
true
|
|
833
|
+
rescue NameError
|
|
834
|
+
false
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
full_message = build_message(message,
|
|
839
|
+
"<?> is alias of\n" +
|
|
840
|
+
"<?> expected",
|
|
841
|
+
alias_method,
|
|
842
|
+
original_method)
|
|
843
|
+
assert_block(full_message) do
|
|
844
|
+
alias_method == original_method
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
792
849
|
##
|
|
793
850
|
# Builds a failure message. +head+ is added before the +template+ and
|
|
794
851
|
# +arguments+ replaces the '?'s positionally in the template.
|
|
@@ -881,23 +938,44 @@ EOT
|
|
|
881
938
|
end
|
|
882
939
|
|
|
883
940
|
MAX_DIFF_TARGET_STRING_SIZE = 1000
|
|
941
|
+
def max_diff_target_string_size
|
|
942
|
+
size = ENV["TEST_UNIT_MAX_DIFF_TARGET_STRING_SIZE"]
|
|
943
|
+
if size
|
|
944
|
+
begin
|
|
945
|
+
size = Integer(size)
|
|
946
|
+
rescue ArgumentError
|
|
947
|
+
size = nil
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
size || MAX_DIFF_TARGET_STRING_SIZE
|
|
951
|
+
end
|
|
952
|
+
|
|
884
953
|
def diff_target_string?(string)
|
|
885
954
|
if string.respond_to?(:bytesize)
|
|
886
|
-
string.bytesize <
|
|
955
|
+
string.bytesize < max_diff_target_string_size
|
|
887
956
|
else
|
|
888
|
-
string.size <
|
|
957
|
+
string.size < max_diff_target_string_size
|
|
958
|
+
end
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
def prepare_for_diff(from, to)
|
|
962
|
+
if !from.is_a?(String) or !to.is_a?(String)
|
|
963
|
+
from = convert(from)
|
|
964
|
+
to = convert(to)
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
if diff_target_string?(from) and diff_target_string?(to)
|
|
968
|
+
[from, to]
|
|
969
|
+
else
|
|
970
|
+
[nil, nil]
|
|
889
971
|
end
|
|
890
972
|
end
|
|
891
973
|
|
|
892
974
|
def delayed_diff(from, to)
|
|
893
975
|
delayed_literal do
|
|
894
|
-
|
|
895
|
-
from = convert(from)
|
|
896
|
-
to = convert(to)
|
|
897
|
-
end
|
|
976
|
+
from, to = prepare_for_diff(from, to)
|
|
898
977
|
|
|
899
|
-
diff = nil
|
|
900
|
-
diff = "" if !diff_target_string?(from) or !diff_target_string?(to)
|
|
978
|
+
diff = "" if from.nil? or to.nil?
|
|
901
979
|
diff ||= Diff.readable(from, to)
|
|
902
980
|
if /^[-+]/ !~ diff
|
|
903
981
|
diff = ""
|
|
@@ -18,6 +18,15 @@ module Test
|
|
|
18
18
|
RUNNERS[id.to_s]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
@@default_runner = nil
|
|
22
|
+
def default_runner
|
|
23
|
+
runner(@@default_runner)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def default_runner=(id)
|
|
27
|
+
@@default_runner = id
|
|
28
|
+
end
|
|
29
|
+
|
|
21
30
|
def register_collector(id, collector_builder=Proc.new)
|
|
22
31
|
COLLECTORS[id] = collector_builder
|
|
23
32
|
COLLECTORS[id.to_s] = collector_builder
|
|
@@ -123,8 +132,6 @@ module Test
|
|
|
123
132
|
puts e
|
|
124
133
|
puts options
|
|
125
134
|
exit(false)
|
|
126
|
-
else
|
|
127
|
-
@filters << proc{false} unless(@filters.empty?)
|
|
128
135
|
end
|
|
129
136
|
not @to_run.empty?
|
|
130
137
|
ensure
|
|
@@ -177,9 +184,9 @@ module Test
|
|
|
177
184
|
n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
|
|
178
185
|
case n
|
|
179
186
|
when Regexp
|
|
180
|
-
@filters << proc{|t| n =~ t.method_name ? true :
|
|
187
|
+
@filters << proc{|t| n =~ t.method_name ? true : false}
|
|
181
188
|
else
|
|
182
|
-
@filters << proc{|t| n == t.method_name
|
|
189
|
+
@filters << proc{|t| n == t.method_name}
|
|
183
190
|
end
|
|
184
191
|
end
|
|
185
192
|
|
|
@@ -189,17 +196,17 @@ module Test
|
|
|
189
196
|
n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
|
|
190
197
|
case n
|
|
191
198
|
when Regexp
|
|
192
|
-
@filters << proc{|t| n =~ t.class.name ? true :
|
|
199
|
+
@filters << proc{|t| n =~ t.class.name ? true : false}
|
|
193
200
|
else
|
|
194
|
-
@filters << proc{|t| n == t.class.name
|
|
201
|
+
@filters << proc{|t| n == t.class.name}
|
|
195
202
|
end
|
|
196
203
|
end
|
|
197
204
|
|
|
198
205
|
priority_filter = Proc.new do |test|
|
|
199
|
-
if @filters
|
|
200
|
-
|
|
206
|
+
if @filters == [priority_filter]
|
|
207
|
+
Priority::Checker.new(test).need_to_run?
|
|
201
208
|
else
|
|
202
|
-
|
|
209
|
+
nil
|
|
203
210
|
end
|
|
204
211
|
end
|
|
205
212
|
o.on("--[no-]priority-mode",
|
|
@@ -325,11 +332,13 @@ module Test
|
|
|
325
332
|
|
|
326
333
|
private
|
|
327
334
|
def default_runner
|
|
335
|
+
runner = self.class.default_runner
|
|
328
336
|
if ENV["EMACS"] == "t"
|
|
329
|
-
self.class.runner(:emacs)
|
|
337
|
+
runner ||= self.class.runner(:emacs)
|
|
330
338
|
else
|
|
331
|
-
self.class.runner(:console)
|
|
339
|
+
runner ||= self.class.runner(:console)
|
|
332
340
|
end
|
|
341
|
+
runner
|
|
333
342
|
end
|
|
334
343
|
|
|
335
344
|
def default_collector
|
|
@@ -23,14 +23,7 @@ module Test
|
|
|
23
23
|
def include?(test)
|
|
24
24
|
return true if(@filters.empty?)
|
|
25
25
|
@filters.each do |filter|
|
|
26
|
-
|
|
27
|
-
if(result.nil?)
|
|
28
|
-
next
|
|
29
|
-
elsif(!result)
|
|
30
|
-
return false
|
|
31
|
-
else
|
|
32
|
-
return true
|
|
33
|
-
end
|
|
26
|
+
return false if filter[test] == false
|
|
34
27
|
end
|
|
35
28
|
true
|
|
36
29
|
end
|
|
@@ -29,7 +29,7 @@ module Test
|
|
|
29
29
|
add_load_path(@base) do
|
|
30
30
|
froms = ["."] if froms.empty?
|
|
31
31
|
test_suites = froms.collect do |from|
|
|
32
|
-
test_suite = collect_recursive(from, find_test_cases)
|
|
32
|
+
test_suite = collect_recursive(resolve_path(from), find_test_cases)
|
|
33
33
|
test_suite = nil if test_suite.tests.empty?
|
|
34
34
|
test_suite
|
|
35
35
|
end.compact
|
|
@@ -56,10 +56,9 @@ module Test
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
private
|
|
59
|
-
def collect_recursive(
|
|
59
|
+
def collect_recursive(path, already_gathered)
|
|
60
60
|
sub_test_suites = []
|
|
61
61
|
|
|
62
|
-
path = resolve_path(name)
|
|
63
62
|
if path.directory?
|
|
64
63
|
directories, files = path.children.partition do |child|
|
|
65
64
|
child.directory?
|
|
@@ -18,7 +18,19 @@ module Test
|
|
|
18
18
|
"case" => Color.new("white", :bold => true) +
|
|
19
19
|
Color.new("blue", :foreground => false),
|
|
20
20
|
"suite" => Color.new("white", :bold => true) +
|
|
21
|
-
Color.new("green", :foreground => false)
|
|
21
|
+
Color.new("green", :foreground => false),
|
|
22
|
+
"diff-inserted-tag" =>
|
|
23
|
+
Color.new("red", :bold => true),
|
|
24
|
+
"diff-deleted-tag" =>
|
|
25
|
+
Color.new("green", :bold => true),
|
|
26
|
+
"diff-difference-tag" =>
|
|
27
|
+
Color.new("cyan", :bold => true),
|
|
28
|
+
"diff-inserted" =>
|
|
29
|
+
Color.new("red", :foreground => false) +
|
|
30
|
+
Color.new("white", :bold => true),
|
|
31
|
+
"diff-deleted" =>
|
|
32
|
+
Color.new("green", :foreground => false) +
|
|
33
|
+
Color.new("white", :bold => true))
|
|
22
34
|
end
|
|
23
35
|
|
|
24
36
|
@@schemes = {}
|
|
@@ -35,7 +35,7 @@ module Test
|
|
|
35
35
|
|
|
36
36
|
def grouped_operations(context_size=nil)
|
|
37
37
|
context_size ||= 3
|
|
38
|
-
_operations = operations
|
|
38
|
+
_operations = operations.dup
|
|
39
39
|
_operations = [[:equal, 0, 0, 0, 0]] if _operations.empty?
|
|
40
40
|
expand_edge_equal_operations!(_operations, context_size)
|
|
41
41
|
|
|
@@ -266,29 +266,187 @@ module Test
|
|
|
266
266
|
end
|
|
267
267
|
end
|
|
268
268
|
|
|
269
|
+
class UTF8Line
|
|
270
|
+
class << self
|
|
271
|
+
# from http://unicode.org/reports/tr11/
|
|
272
|
+
WIDE_CHARACTERS =
|
|
273
|
+
[0x1100..0x1159, 0x115F..0x115F, 0x2329..0x232A,
|
|
274
|
+
0x2E80..0x2E99, 0x2E9B..0x2EF3, 0x2F00..0x2FD5,
|
|
275
|
+
0x2FF0..0x2FFB, 0x3000..0x303E, 0x3041..0x3096,
|
|
276
|
+
0x3099..0x30FF, 0x3105..0x312D, 0x3131..0x318E,
|
|
277
|
+
0x3190..0x31B7, 0x31C0..0x31E3, 0x31F0..0x321E,
|
|
278
|
+
0x3220..0x3243, 0x3250..0x32FE, 0x3300..0x4DB5,
|
|
279
|
+
0x4E00..0x9FC3, 0xA000..0xA48C, 0xA490..0xA4C6,
|
|
280
|
+
0xAC00..0xD7A3, 0xF900..0xFA2D, 0xFA30..0xFA6A,
|
|
281
|
+
0xFA70..0xFAD9, 0xFE10..0xFE19, 0xFE30..0xFE52,
|
|
282
|
+
0xFE54..0xFE66, 0xFE68..0xFE6B, 0xFF01..0xFF60,
|
|
283
|
+
0xFFE0..0xFFE6, 0x20000..0x2FFFD, 0x30000..0x3FFFD,
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
AMBIGUOUS =
|
|
287
|
+
[0x00A1..0x00A1, 0x00A4..0x00A4, 0x00A7..0x00A8,
|
|
288
|
+
0x00AA..0x00AA, 0x00AD..0x00AE, 0x00B0..0x00B4,
|
|
289
|
+
0x00B6..0x00BA, 0x00BC..0x00BF, 0x00C6..0x00C6,
|
|
290
|
+
0x00D0..0x00D0, 0x00D7..0x00D8, 0x00DE..0x00E1,
|
|
291
|
+
0x00E6..0x00E6, 0x00E8..0x00EA, 0x00EC..0x00ED,
|
|
292
|
+
0x00F0..0x00F0, 0x00F2..0x00F3, 0x00F7..0x00FA,
|
|
293
|
+
0x00FC..0x00FC, 0x00FE..0x00FE, 0x0101..0x0101,
|
|
294
|
+
0x0111..0x0111, 0x0113..0x0113, 0x011B..0x011B,
|
|
295
|
+
0x0126..0x0127, 0x012B..0x012B, 0x0131..0x0133,
|
|
296
|
+
0x0138..0x0138, 0x013F..0x0142, 0x0144..0x0144,
|
|
297
|
+
0x0148..0x014B, 0x014D..0x014D, 0x0152..0x0153,
|
|
298
|
+
0x0166..0x0167, 0x016B..0x016B, 0x01CE..0x01CE,
|
|
299
|
+
0x01D0..0x01D0, 0x01D2..0x01D2, 0x01D4..0x01D4,
|
|
300
|
+
0x01D6..0x01D6, 0x01D8..0x01D8, 0x01DA..0x01DA,
|
|
301
|
+
0x01DC..0x01DC, 0x0251..0x0251, 0x0261..0x0261,
|
|
302
|
+
0x02C4..0x02C4, 0x02C7..0x02C7, 0x02C9..0x02CB,
|
|
303
|
+
0x02CD..0x02CD, 0x02D0..0x02D0, 0x02D8..0x02DB,
|
|
304
|
+
0x02DD..0x02DD, 0x02DF..0x02DF, 0x0300..0x036F,
|
|
305
|
+
0x0391..0x03A1, 0x03A3..0x03A9, 0x03B1..0x03C1,
|
|
306
|
+
0x03C3..0x03C9, 0x0401..0x0401, 0x0410..0x044F,
|
|
307
|
+
0x0451..0x0451, 0x2010..0x2010, 0x2013..0x2016,
|
|
308
|
+
0x2018..0x2019, 0x201C..0x201D, 0x2020..0x2022,
|
|
309
|
+
0x2024..0x2027, 0x2030..0x2030, 0x2032..0x2033,
|
|
310
|
+
0x2035..0x2035, 0x203B..0x203B, 0x203E..0x203E,
|
|
311
|
+
0x2074..0x2074, 0x207F..0x207F, 0x2081..0x2084,
|
|
312
|
+
0x20AC..0x20AC, 0x2103..0x2103, 0x2105..0x2105,
|
|
313
|
+
0x2109..0x2109, 0x2113..0x2113, 0x2116..0x2116,
|
|
314
|
+
0x2121..0x2122, 0x2126..0x2126, 0x212B..0x212B,
|
|
315
|
+
0x2153..0x2154, 0x215B..0x215E, 0x2160..0x216B,
|
|
316
|
+
0x2170..0x2179, 0x2190..0x2199, 0x21B8..0x21B9,
|
|
317
|
+
0x21D2..0x21D2, 0x21D4..0x21D4, 0x21E7..0x21E7,
|
|
318
|
+
0x2200..0x2200, 0x2202..0x2203, 0x2207..0x2208,
|
|
319
|
+
0x220B..0x220B, 0x220F..0x220F, 0x2211..0x2211,
|
|
320
|
+
0x2215..0x2215, 0x221A..0x221A, 0x221D..0x2220,
|
|
321
|
+
0x2223..0x2223, 0x2225..0x2225, 0x2227..0x222C,
|
|
322
|
+
0x222E..0x222E, 0x2234..0x2237, 0x223C..0x223D,
|
|
323
|
+
0x2248..0x2248, 0x224C..0x224C, 0x2252..0x2252,
|
|
324
|
+
0x2260..0x2261, 0x2264..0x2267, 0x226A..0x226B,
|
|
325
|
+
0x226E..0x226F, 0x2282..0x2283, 0x2286..0x2287,
|
|
326
|
+
0x2295..0x2295, 0x2299..0x2299, 0x22A5..0x22A5,
|
|
327
|
+
0x22BF..0x22BF, 0x2312..0x2312, 0x2460..0x24E9,
|
|
328
|
+
0x24EB..0x254B, 0x2550..0x2573, 0x2580..0x258F,
|
|
329
|
+
0x2592..0x2595, 0x25A0..0x25A1, 0x25A3..0x25A9,
|
|
330
|
+
0x25B2..0x25B3, 0x25B6..0x25B7, 0x25BC..0x25BD,
|
|
331
|
+
0x25C0..0x25C1, 0x25C6..0x25C8, 0x25CB..0x25CB,
|
|
332
|
+
0x25CE..0x25D1, 0x25E2..0x25E5, 0x25EF..0x25EF,
|
|
333
|
+
0x2605..0x2606, 0x2609..0x2609, 0x260E..0x260F,
|
|
334
|
+
0x2614..0x2615, 0x261C..0x261C, 0x261E..0x261E,
|
|
335
|
+
0x2640..0x2640, 0x2642..0x2642, 0x2660..0x2661,
|
|
336
|
+
0x2663..0x2665, 0x2667..0x266A, 0x266C..0x266D,
|
|
337
|
+
0x266F..0x266F, 0x273D..0x273D, 0x2776..0x277F,
|
|
338
|
+
0xE000..0xF8FF, 0xFE00..0xFE0F, 0xFFFD..0xFFFD,
|
|
339
|
+
0xE0100..0xE01EF, 0xF0000..0xFFFFD, 0x100000..0x10FFFD,
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
def wide_character?(character)
|
|
343
|
+
binary_search_ranges(character, WIDE_CHARACTERS) or
|
|
344
|
+
binary_search_ranges(character, AMBIGUOUS)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
private
|
|
348
|
+
def binary_search_ranges(character, ranges)
|
|
349
|
+
if ranges.size.zero?
|
|
350
|
+
false
|
|
351
|
+
elsif ranges.size == 1
|
|
352
|
+
ranges[0].include?(character)
|
|
353
|
+
else
|
|
354
|
+
half = ranges.size / 2
|
|
355
|
+
range = ranges[half]
|
|
356
|
+
if range.include?(character)
|
|
357
|
+
true
|
|
358
|
+
elsif character < range.begin
|
|
359
|
+
binary_search_ranges(character, ranges[0...half])
|
|
360
|
+
else
|
|
361
|
+
binary_search_ranges(character, ranges[(half + 1)..-1])
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def initialize(line)
|
|
368
|
+
@line = line
|
|
369
|
+
@characters = @line.unpack("U*")
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def [](*args)
|
|
373
|
+
result = @characters[*args]
|
|
374
|
+
if result.respond_to?(:pack)
|
|
375
|
+
result.pack("U*")
|
|
376
|
+
else
|
|
377
|
+
result
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def each(&block)
|
|
382
|
+
@characters.each(&block)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def size
|
|
386
|
+
@characters.size
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def to_s
|
|
390
|
+
@line
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def compute_width(start, _end)
|
|
394
|
+
width = 0
|
|
395
|
+
start.upto(_end - 1) do |i|
|
|
396
|
+
if self.class.wide_character?(@characters[i])
|
|
397
|
+
width += 2
|
|
398
|
+
else
|
|
399
|
+
width += 1
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
width
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
269
406
|
class ReadableDiffer < Differ
|
|
270
407
|
def diff(options={})
|
|
271
|
-
result = []
|
|
272
|
-
|
|
273
|
-
matcher.operations.each do |args|
|
|
274
|
-
tag, from_start, from_end, to_start, to_end = args
|
|
408
|
+
@result = []
|
|
409
|
+
operations.each do |tag, from_start, from_end, to_start, to_end|
|
|
275
410
|
case tag
|
|
276
411
|
when :replace
|
|
277
|
-
|
|
412
|
+
diff_lines(from_start, from_end, to_start, to_end)
|
|
278
413
|
when :delete
|
|
279
|
-
|
|
414
|
+
tag_deleted(@from[from_start...from_end])
|
|
280
415
|
when :insert
|
|
281
|
-
|
|
416
|
+
tag_inserted(@to[to_start...to_end])
|
|
282
417
|
when :equal
|
|
283
|
-
|
|
418
|
+
tag_equal(@from[from_start...from_end])
|
|
284
419
|
else
|
|
285
420
|
raise "unknown tag: #{tag}"
|
|
286
421
|
end
|
|
287
422
|
end
|
|
288
|
-
result
|
|
423
|
+
@result
|
|
289
424
|
end
|
|
290
425
|
|
|
291
426
|
private
|
|
427
|
+
def operations
|
|
428
|
+
@operations ||= nil
|
|
429
|
+
if @operations.nil?
|
|
430
|
+
matcher = SequenceMatcher.new(@from, @to)
|
|
431
|
+
@operations = matcher.operations
|
|
432
|
+
end
|
|
433
|
+
@operations
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def default_ratio
|
|
437
|
+
0.74
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def cut_off_ratio
|
|
441
|
+
0.75
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def tag(mark, contents)
|
|
445
|
+
contents.each do |content|
|
|
446
|
+
@result << "#{mark}#{content}"
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
292
450
|
def tag_deleted(contents)
|
|
293
451
|
tag("- ", contents)
|
|
294
452
|
end
|
|
@@ -306,7 +464,7 @@ module Test
|
|
|
306
464
|
end
|
|
307
465
|
|
|
308
466
|
def find_diff_line_info(from_start, from_end, to_start, to_end)
|
|
309
|
-
best_ratio =
|
|
467
|
+
best_ratio = default_ratio
|
|
310
468
|
from_equal_index = to_equal_index = nil
|
|
311
469
|
from_best_index = to_best_index = nil
|
|
312
470
|
|
|
@@ -334,30 +492,31 @@ module Test
|
|
|
334
492
|
end
|
|
335
493
|
|
|
336
494
|
def diff_lines(from_start, from_end, to_start, to_end)
|
|
337
|
-
cut_off = 0.75
|
|
338
|
-
|
|
339
495
|
info = find_diff_line_info(from_start, from_end, to_start, to_end)
|
|
340
496
|
best_ratio, from_equal_index, to_equal_index, *info = info
|
|
341
497
|
from_best_index, to_best_index = info
|
|
498
|
+
from_best_index ||= from_start
|
|
499
|
+
to_best_index ||= to_start
|
|
342
500
|
|
|
343
|
-
if best_ratio <
|
|
501
|
+
if best_ratio < cut_off_ratio
|
|
344
502
|
if from_equal_index.nil?
|
|
345
|
-
tagged_from = tag_deleted(@from[from_start...from_end])
|
|
346
|
-
tagged_to = tag_inserted(@to[to_start...to_end])
|
|
347
503
|
if to_end - to_start < from_end - from_start
|
|
348
|
-
|
|
504
|
+
tag_inserted(@to[to_start...to_end])
|
|
505
|
+
tag_deleted(@from[from_start...from_end])
|
|
349
506
|
else
|
|
350
|
-
|
|
507
|
+
tag_deleted(@from[from_start...from_end])
|
|
508
|
+
tag_inserted(@to[to_start...to_end])
|
|
351
509
|
end
|
|
510
|
+
return
|
|
352
511
|
end
|
|
353
512
|
from_best_index = from_equal_index
|
|
354
513
|
to_best_index = to_equal_index
|
|
355
514
|
best_ratio = 1.0
|
|
356
515
|
end
|
|
357
516
|
|
|
358
|
-
_diff_lines(from_start, from_best_index, to_start, to_best_index)
|
|
359
|
-
|
|
360
|
-
|
|
517
|
+
_diff_lines(from_start, from_best_index, to_start, to_best_index)
|
|
518
|
+
diff_line(@from[from_best_index], @to[to_best_index])
|
|
519
|
+
_diff_lines(from_best_index + 1, from_end, to_best_index + 1, to_end)
|
|
361
520
|
end
|
|
362
521
|
|
|
363
522
|
def _diff_lines(from_start, from_end, to_start, to_end)
|
|
@@ -372,26 +531,54 @@ module Test
|
|
|
372
531
|
end
|
|
373
532
|
end
|
|
374
533
|
|
|
534
|
+
def line_operations(from_line, to_line)
|
|
535
|
+
if !from_line.respond_to?(:force_encoding) and $KCODE == "UTF8"
|
|
536
|
+
from_line = UTF8Line.new(from_line)
|
|
537
|
+
to_line = UTF8Line.new(to_line)
|
|
538
|
+
end
|
|
539
|
+
matcher = SequenceMatcher.new(from_line, to_line,
|
|
540
|
+
&method(:space_character?))
|
|
541
|
+
[from_line, to_line, matcher.operations]
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def compute_width(line, start, _end)
|
|
545
|
+
if line.respond_to?(:encoding) and
|
|
546
|
+
Encoding.compatible?(Encoding::UTF_8, line.encoding)
|
|
547
|
+
utf8_line = line[start..._end].encode(Encoding::UTF_8)
|
|
548
|
+
width = 0
|
|
549
|
+
utf8_line.each_codepoint do |unicode_codepoint|
|
|
550
|
+
if UTF8Line.wide_character?(unicode_codepoint)
|
|
551
|
+
width += 2
|
|
552
|
+
else
|
|
553
|
+
width += 1
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
width
|
|
557
|
+
elsif line.is_a?(UTF8Line)
|
|
558
|
+
line.compute_width(start, _end)
|
|
559
|
+
else
|
|
560
|
+
_end - start
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
375
564
|
def diff_line(from_line, to_line)
|
|
376
565
|
from_tags = ""
|
|
377
566
|
to_tags = ""
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
from_length = from_end - from_start
|
|
383
|
-
to_length = to_end - to_start
|
|
567
|
+
from_line, to_line, _operations = line_operations(from_line, to_line)
|
|
568
|
+
_operations.each do |tag, from_start, from_end, to_start, to_end|
|
|
569
|
+
from_width = compute_width(from_line, from_start, from_end)
|
|
570
|
+
to_width = compute_width(to_line, to_start, to_end)
|
|
384
571
|
case tag
|
|
385
572
|
when :replace
|
|
386
|
-
from_tags << "^" *
|
|
387
|
-
to_tags << "^" *
|
|
573
|
+
from_tags << "^" * from_width
|
|
574
|
+
to_tags << "^" * to_width
|
|
388
575
|
when :delete
|
|
389
|
-
from_tags << "-" *
|
|
576
|
+
from_tags << "-" * from_width
|
|
390
577
|
when :insert
|
|
391
|
-
to_tags << "+" *
|
|
578
|
+
to_tags << "+" * to_width
|
|
392
579
|
when :equal
|
|
393
|
-
from_tags << " " *
|
|
394
|
-
to_tags << " " *
|
|
580
|
+
from_tags << " " * from_width
|
|
581
|
+
to_tags << " " * to_width
|
|
395
582
|
else
|
|
396
583
|
raise "unknown tag: #{tag}"
|
|
397
584
|
end
|
|
@@ -409,13 +596,12 @@ module Test
|
|
|
409
596
|
|
|
410
597
|
result = tag_deleted([from_line])
|
|
411
598
|
unless from_tags.empty?
|
|
412
|
-
|
|
599
|
+
tag_difference(["#{"\t" * common}#{from_tags}"])
|
|
413
600
|
end
|
|
414
|
-
|
|
601
|
+
tag_inserted([to_line])
|
|
415
602
|
unless to_tags.empty?
|
|
416
|
-
|
|
603
|
+
tag_difference(["#{"\t" * common}#{to_tags}"])
|
|
417
604
|
end
|
|
418
|
-
result
|
|
419
605
|
end
|
|
420
606
|
|
|
421
607
|
def n_leading_characters(string, character)
|