rroonga 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS.ja.rdoc +18 -0
- data/NEWS.rdoc +18 -0
- data/bin/grndump +71 -0
- data/ext/groonga/rb-grn-accessor.c +64 -1
- data/ext/groonga/rb-grn-context.c +40 -1
- data/ext/groonga/rb-grn-database.c +47 -5
- data/ext/groonga/rb-grn-index-column.c +77 -7
- data/ext/groonga/rb-grn-object.c +8 -4
- data/ext/groonga/rb-grn-table-cursor.c +56 -1
- data/ext/groonga/rb-grn-table-key-support.c +2 -2
- data/ext/groonga/rb-grn-table.c +10 -38
- data/ext/groonga/rb-grn.h +11 -1
- data/html/index.html +6 -6
- data/html/ranguba.css +8 -1
- data/lib/groonga.rb +1 -0
- data/lib/groonga/dumper.rb +481 -0
- data/lib/groonga/schema.rb +54 -181
- data/rroonga-build.rb +1 -1
- data/test-unit-notify/Rakefile +47 -0
- data/test-unit-notify/lib/test/unit/notify.rb +104 -0
- data/test-unit/COPYING +56 -0
- data/test-unit/GPL +340 -0
- data/test-unit/PSFL +271 -0
- data/test-unit/Rakefile +18 -5
- data/test-unit/html/bar.svg +153 -0
- data/test-unit/html/developer.svg +469 -0
- data/test-unit/html/favicon.ico +0 -0
- data/test-unit/html/favicon.svg +82 -0
- data/test-unit/html/heading-mark.svg +393 -0
- data/test-unit/html/index.html +235 -13
- data/test-unit/html/index.html.ja +258 -15
- data/test-unit/html/install.svg +636 -0
- data/test-unit/html/logo.svg +483 -0
- data/test-unit/html/test-unit.css +339 -0
- data/test-unit/html/tutorial.svg +559 -0
- data/test-unit/lib/test/unit.rb +6 -1
- data/test-unit/lib/test/unit/assertions.rb +115 -11
- data/test-unit/lib/test/unit/autorunner.rb +5 -2
- data/test-unit/lib/test/unit/collector/load.rb +1 -1
- data/test-unit/lib/test/unit/color-scheme.rb +6 -2
- data/test-unit/lib/test/unit/diff.rb +17 -1
- data/test-unit/lib/test/unit/testcase.rb +7 -0
- data/test-unit/lib/test/unit/testresult.rb +34 -2
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +9 -45
- data/test-unit/lib/test/unit/ui/tap/testrunner.rb +2 -12
- data/test-unit/lib/test/unit/ui/testrunner.rb +25 -0
- data/test-unit/lib/test/unit/util/backtracefilter.rb +1 -0
- data/test-unit/lib/test/unit/util/output.rb +31 -0
- data/test-unit/lib/test/unit/version.rb +1 -1
- data/test-unit/test/test-color-scheme.rb +4 -2
- data/test-unit/test/test_assertions.rb +51 -5
- data/test-unit/test/ui/test_tap.rb +33 -0
- data/test-unit/test/util/test-output.rb +11 -0
- data/test/groonga-test-utils.rb +1 -0
- data/test/test-accessor.rb +32 -0
- data/test/test-context.rb +7 -1
- data/test/test-database-dumper.rb +156 -0
- data/test/test-index-column.rb +67 -1
- data/test/test-schema-dumper.rb +181 -0
- data/test/test-schema.rb +53 -97
- data/test/test-table-dumper.rb +83 -0
- metadata +48 -11
- data/ext/groonga/mkmf.log +0 -99
- data/test-unit/html/classic.html +0 -15
data/test-unit/lib/test/unit.rb
CHANGED
@@ -90,8 +90,13 @@ module Test # :nodoc:
|
|
90
90
|
#
|
91
91
|
# Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free
|
92
92
|
# software, and is distributed under the Ruby license. See the COPYING
|
93
|
-
# file
|
93
|
+
# file.
|
94
94
|
#
|
95
|
+
# Exception: lib/test/unit/diff.rb is copyright (c)
|
96
|
+
# 2008-2010 Kouhei Sutou and 2001-2008 Python Software
|
97
|
+
# Foundation. It is free software, and is distributed
|
98
|
+
# under the Ruby license and/or the PSF license. See the
|
99
|
+
# COPYING file and PSFL file.
|
95
100
|
#
|
96
101
|
# == Warranty
|
97
102
|
#
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Author:: Nathaniel Talbott.
|
2
2
|
# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
|
3
|
-
# Copyright (c) 2009 Kouhei Sutou.
|
3
|
+
# Copyright (c) 2009-2010 Kouhei Sutou. All rights reserved.
|
4
4
|
# License:: Ruby license.
|
5
5
|
|
6
6
|
require 'test/unit/assertionfailederror'
|
@@ -80,10 +80,23 @@ module Test
|
|
80
80
|
public
|
81
81
|
def assert_equal(expected, actual, message=nil)
|
82
82
|
diff = AssertionMessage.delayed_diff(expected, actual)
|
83
|
-
|
83
|
+
if expected.respond_to?(:encoding) and
|
84
|
+
actual.respond_to?(:encoding) and
|
85
|
+
expected.encoding != actual.encoding
|
86
|
+
format = <<EOT
|
87
|
+
<?>(?) expected but was
|
88
|
+
<?>(?).?
|
89
|
+
EOT
|
90
|
+
full_message = build_message(message, format,
|
91
|
+
expected, expected.encoding.name,
|
92
|
+
actual, actual.encoding.name,
|
93
|
+
diff)
|
94
|
+
else
|
95
|
+
full_message = build_message(message, <<EOT, expected, actual, diff)
|
84
96
|
<?> expected but was
|
85
97
|
<?>.?
|
86
98
|
EOT
|
99
|
+
end
|
87
100
|
begin
|
88
101
|
assert_block(full_message) { expected == actual }
|
89
102
|
rescue AssertionFailedError => failure
|
@@ -539,18 +552,73 @@ EOT
|
|
539
552
|
public
|
540
553
|
def assert_in_delta(expected_float, actual_float, delta, message="")
|
541
554
|
_wrap_assertion do
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
555
|
+
_assert_in_delta_validate_arguments(expected_float,
|
556
|
+
actual_float,
|
557
|
+
delta)
|
558
|
+
full_message = _assert_in_delta_message(expected_float,
|
559
|
+
actual_float,
|
560
|
+
delta,
|
561
|
+
message)
|
562
|
+
assert_block(full_message) do
|
563
|
+
(expected_float.to_f - actual_float.to_f).abs <= delta.to_f
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
# :stopdoc:
|
569
|
+
private
|
570
|
+
def _assert_in_delta_validate_arguments(expected_float,
|
571
|
+
actual_float,
|
572
|
+
delta)
|
573
|
+
{
|
574
|
+
expected_float => "first float",
|
575
|
+
actual_float => "second float",
|
576
|
+
delta => "delta"
|
577
|
+
}.each do |float, name|
|
578
|
+
assert_respond_to(float, :to_f,
|
579
|
+
"The arguments must respond to to_f; " +
|
580
|
+
"the #{name} did not")
|
581
|
+
end
|
582
|
+
assert_operator(delta, :>=, 0.0, "The delta should not be negative")
|
583
|
+
end
|
584
|
+
|
585
|
+
def _assert_in_delta_message(expected_float, actual_float, delta,
|
586
|
+
message)
|
587
|
+
format = <<-EOT
|
588
|
+
<?> expected but was
|
589
|
+
<?> (tolerance <?>).
|
550
590
|
EOT
|
551
|
-
|
591
|
+
arguments = [expected_float, actual_float, delta]
|
592
|
+
normalized_expected = expected_float.to_f
|
593
|
+
normalized_actual = actual_float.to_f
|
594
|
+
normalized_delta = delta.to_f
|
595
|
+
relation_format = nil
|
596
|
+
relation_arguments = nil
|
597
|
+
if normalized_actual < normalized_expected - normalized_delta
|
598
|
+
relation_format = "<<?> < <?>-<?>(?) <= <?>+<?>(?)>"
|
599
|
+
relation_arguments = [actual_float,
|
600
|
+
expected_float, delta, expected_float - delta,
|
601
|
+
expected_float, delta, expected_float + delta]
|
602
|
+
elsif normalized_expected - normalized_delta < normalized_actual
|
603
|
+
relation_format = "<<?>-<?>(?) <= <?>+<?>(?) < <?>>"
|
604
|
+
relation_arguments = [expected_float, delta, expected_float - delta,
|
605
|
+
expected_float, delta, expected_float + delta,
|
606
|
+
actual_float]
|
552
607
|
end
|
608
|
+
|
609
|
+
if relation_format
|
610
|
+
format << <<-EOT
|
611
|
+
|
612
|
+
Relation:
|
613
|
+
#{relation_format}
|
614
|
+
EOT
|
615
|
+
arguments.concat(relation_arguments)
|
616
|
+
end
|
617
|
+
|
618
|
+
build_message(message, format, *arguments)
|
553
619
|
end
|
620
|
+
public
|
621
|
+
# :startdoc:
|
554
622
|
|
555
623
|
##
|
556
624
|
# Passes if the method send returns a true value.
|
@@ -846,6 +914,42 @@ EOT
|
|
846
914
|
end
|
847
915
|
end
|
848
916
|
|
917
|
+
##
|
918
|
+
# Passes if +path+ exists.
|
919
|
+
#
|
920
|
+
# Example:
|
921
|
+
# assert_path_exist("/tmp") # -> pass
|
922
|
+
# assert_path_exist("/bin/sh") # -> pass
|
923
|
+
# assert_path_exist("/nonexistent") # -> fail
|
924
|
+
def assert_path_exist(path, message=nil)
|
925
|
+
_wrap_assertion do
|
926
|
+
failure_message = build_message(message,
|
927
|
+
"<?> expected to exist",
|
928
|
+
path)
|
929
|
+
assert_block(failure_message) do
|
930
|
+
File.exist?(path)
|
931
|
+
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
##
|
936
|
+
# Passes if +path+ doesn't exist.
|
937
|
+
#
|
938
|
+
# Example:
|
939
|
+
# assert_path_not_exist("/nonexistent") # -> pass
|
940
|
+
# assert_path_not_exist("/tmp") # -> fail
|
941
|
+
# assert_path_not_exist("/bin/sh") # -> fail
|
942
|
+
def assert_path_not_exist(path, message=nil)
|
943
|
+
_wrap_assertion do
|
944
|
+
failure_message = build_message(message,
|
945
|
+
"<?> expected to not exist",
|
946
|
+
path)
|
947
|
+
assert_block(failure_message) do
|
948
|
+
not File.exist?(path)
|
949
|
+
end
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
849
953
|
##
|
850
954
|
# Builds a failure message. +head+ is added before the +template+ and
|
851
955
|
# +arguments+ replaces the '?'s positionally in the template.
|
@@ -99,7 +99,7 @@ module Test
|
|
99
99
|
|
100
100
|
attr_reader :suite, :runner_options
|
101
101
|
attr_accessor :filters, :to_run, :pattern, :exclude, :base, :workdir
|
102
|
-
attr_accessor :color_scheme
|
102
|
+
attr_accessor :color_scheme, :listeners
|
103
103
|
attr_writer :runner, :collector
|
104
104
|
|
105
105
|
def initialize(standalone)
|
@@ -113,6 +113,7 @@ module Test
|
|
113
113
|
@runner_options = {}
|
114
114
|
@default_arguments = []
|
115
115
|
@workdir = nil
|
116
|
+
@listeners = []
|
116
117
|
config_file = "test-unit.yml"
|
117
118
|
if File.exist?(config_file)
|
118
119
|
load_config(config_file)
|
@@ -300,6 +301,8 @@ module Test
|
|
300
301
|
runner = @runner[self]
|
301
302
|
return false if runner.nil?
|
302
303
|
@runner_options[:color_scheme] ||= @color_scheme
|
304
|
+
@runner_options[:listeners] ||= []
|
305
|
+
@runner_options[:listeners].concat(@listeners)
|
303
306
|
Dir.chdir(@workdir) if @workdir
|
304
307
|
runner.run(suite, @runner_options).passed?
|
305
308
|
end
|
@@ -342,7 +345,7 @@ module Test
|
|
342
345
|
end
|
343
346
|
|
344
347
|
def global_config_file
|
345
|
-
File.expand_path("~/.test-unit.
|
348
|
+
File.expand_path("~/.test-unit.yml")
|
346
349
|
rescue ArgumentError
|
347
350
|
nil
|
348
351
|
end
|
@@ -95,7 +95,7 @@ module Test
|
|
95
95
|
|
96
96
|
def collect_file(path, test_suites, already_gathered)
|
97
97
|
@program_file ||= File.expand_path($0)
|
98
|
-
return if @program_file == path.to_s
|
98
|
+
return if @program_file == path.expand_path.to_s
|
99
99
|
add_load_path(path.expand_path.dirname) do
|
100
100
|
require(path.to_s)
|
101
101
|
find_test_cases(already_gathered).each do |test_case|
|
@@ -8,8 +8,12 @@ module Test
|
|
8
8
|
class << self
|
9
9
|
@@default = nil
|
10
10
|
def default
|
11
|
-
@@default ||= new("
|
12
|
-
|
11
|
+
@@default ||= new("pass" =>
|
12
|
+
Color.new("green", :foreground => false) +
|
13
|
+
Color.new("white", :bold => true),
|
14
|
+
"failure" =>
|
15
|
+
Color.new("red", :foreground => false) +
|
16
|
+
Color.new("white", :bold => true),
|
13
17
|
"pending" => Color.new("magenta", :bold => true),
|
14
18
|
"omission" => Color.new("blue", :bold => true),
|
15
19
|
"notification" => Color.new("cyan", :bold => true),
|
@@ -1,4 +1,11 @@
|
|
1
1
|
# port of Python's difflib.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2001-2008 Python Software Foundation; All Rights Reserved
|
4
|
+
# Copyright (c) 2008-2010 Kouhei Sutou; All Rights Reserved
|
5
|
+
#
|
6
|
+
# It is free software, and is distributed under the Ruby
|
7
|
+
# license and/or the PSF license. See the COPYING file and
|
8
|
+
# PSFL file.
|
2
9
|
|
3
10
|
module Test
|
4
11
|
module Unit
|
@@ -717,7 +724,16 @@ module Test
|
|
717
724
|
|
718
725
|
def diff(differ_class, from, to, options={})
|
719
726
|
differ = differ_class.new(from.split(/\r?\n/), to.split(/\r?\n/))
|
720
|
-
differ.diff(options)
|
727
|
+
lines = differ.diff(options)
|
728
|
+
if Object.const_defined?(:EncodingError)
|
729
|
+
begin
|
730
|
+
lines.join("\n")
|
731
|
+
rescue EncodingError
|
732
|
+
lines.collect {|line| line.force_encoding("ASCII-8BIT")}.join("\n")
|
733
|
+
end
|
734
|
+
else
|
735
|
+
lines.join("\n")
|
736
|
+
end
|
721
737
|
end
|
722
738
|
end
|
723
739
|
end
|
@@ -19,6 +19,7 @@ require 'test/unit/priority'
|
|
19
19
|
require 'test/unit/testsuite'
|
20
20
|
require 'test/unit/assertionfailederror'
|
21
21
|
require 'test/unit/util/backtracefilter'
|
22
|
+
require 'test/unit/util/output'
|
22
23
|
require 'test/unit/util/method-owner-finder'
|
23
24
|
|
24
25
|
module Test
|
@@ -82,6 +83,7 @@ module Test
|
|
82
83
|
include Priority
|
83
84
|
include Assertions
|
84
85
|
include Util::BacktraceFilter
|
86
|
+
include Util::Output
|
85
87
|
|
86
88
|
STARTED = name + "::STARTED" # :nodoc:
|
87
89
|
FINISHED = name + "::FINISHED" # :nodoc:
|
@@ -448,6 +450,7 @@ module Test
|
|
448
450
|
notify("#{self.class}\##{@method_name} was redefined")
|
449
451
|
end
|
450
452
|
__send__(@method_name)
|
453
|
+
add_pass
|
451
454
|
end
|
452
455
|
|
453
456
|
def handle_exception(exception)
|
@@ -471,6 +474,10 @@ module Test
|
|
471
474
|
def add_assertion
|
472
475
|
current_result.add_assertion
|
473
476
|
end
|
477
|
+
|
478
|
+
def add_pass
|
479
|
+
current_result.add_pass
|
480
|
+
end
|
474
481
|
end
|
475
482
|
end
|
476
483
|
end
|
@@ -34,11 +34,11 @@ module Test
|
|
34
34
|
CHANGED = "CHANGED"
|
35
35
|
FAULT = "FAULT"
|
36
36
|
|
37
|
-
attr_reader :run_count, :assertion_count, :faults
|
37
|
+
attr_reader :run_count, :pass_count, :assertion_count, :faults
|
38
38
|
|
39
39
|
# Constructs a new, empty TestResult.
|
40
40
|
def initialize
|
41
|
-
@run_count, @assertion_count = 0, 0
|
41
|
+
@run_count, @pass_count, @assertion_count = 0, 0, 0
|
42
42
|
@summary_generators = []
|
43
43
|
@problem_checkers = []
|
44
44
|
@faults = []
|
@@ -51,6 +51,10 @@ module Test
|
|
51
51
|
notify_changed
|
52
52
|
end
|
53
53
|
|
54
|
+
def add_pass
|
55
|
+
@pass_count += 1
|
56
|
+
end
|
57
|
+
|
54
58
|
# Records an individual assertion.
|
55
59
|
def add_assertion
|
56
60
|
@assertion_count += 1
|
@@ -65,6 +69,25 @@ module Test
|
|
65
69
|
*@summary_generators.collect {|generator| send(generator)}].join(", ")
|
66
70
|
end
|
67
71
|
|
72
|
+
# Returnes a string that shows result status.
|
73
|
+
def status
|
74
|
+
if passed?
|
75
|
+
if pending_count > 0
|
76
|
+
"pending"
|
77
|
+
elsif omission_count > 0
|
78
|
+
"omission"
|
79
|
+
elsif notification_count > 0
|
80
|
+
"notification"
|
81
|
+
else
|
82
|
+
"pass"
|
83
|
+
end
|
84
|
+
elsif error_count > 0
|
85
|
+
"error"
|
86
|
+
elsif failure_count > 0
|
87
|
+
"failure"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
68
91
|
def to_s
|
69
92
|
summary
|
70
93
|
end
|
@@ -75,6 +98,15 @@ module Test
|
|
75
98
|
@problem_checkers.all? {|checker| not send(checker)}
|
76
99
|
end
|
77
100
|
|
101
|
+
def pass_percentage
|
102
|
+
n_tests = @run_count - omission_count
|
103
|
+
if n_tests.zero?
|
104
|
+
0
|
105
|
+
else
|
106
|
+
100.0 * (@pass_count / n_tests.to_f)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
78
110
|
private
|
79
111
|
def notify_changed
|
80
112
|
notify_listeners(CHANGED, self)
|
@@ -38,23 +38,14 @@ module Test
|
|
38
38
|
@progress_row_max = @options[:progress_row_max]
|
39
39
|
@progress_row_max ||= guess_progress_row_max
|
40
40
|
@already_outputted = false
|
41
|
-
@n_successes = 0
|
42
|
-
@n_omissions = 0
|
43
41
|
@indent = 0
|
44
42
|
@top_level = true
|
45
43
|
@faults = []
|
46
44
|
end
|
47
45
|
|
48
|
-
# Begins the test run.
|
49
|
-
def start
|
50
|
-
setup_mediator
|
51
|
-
attach_to_mediator
|
52
|
-
return start_mediator
|
53
|
-
end
|
54
|
-
|
55
46
|
private
|
56
47
|
def setup_mediator
|
57
|
-
|
48
|
+
super
|
58
49
|
output_setup_end
|
59
50
|
end
|
60
51
|
|
@@ -64,10 +55,6 @@ module Test
|
|
64
55
|
output("Loaded suite #{suite_name}")
|
65
56
|
end
|
66
57
|
|
67
|
-
def create_mediator(suite)
|
68
|
-
return TestRunnerMediator.new(suite)
|
69
|
-
end
|
70
|
-
|
71
58
|
def attach_to_mediator
|
72
59
|
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
73
60
|
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
@@ -78,14 +65,9 @@ module Test
|
|
78
65
|
@mediator.add_listener(TestSuite::FINISHED, &method(:test_suite_finished))
|
79
66
|
end
|
80
67
|
|
81
|
-
def start_mediator
|
82
|
-
return @mediator.run_suite
|
83
|
-
end
|
84
|
-
|
85
68
|
def add_fault(fault)
|
86
69
|
@faults << fault
|
87
70
|
output_progress(fault.single_character_display, fault_color(fault))
|
88
|
-
@n_omissions += 1 if fault.is_a?(Omission)
|
89
71
|
@already_outputted = true if fault.critical?
|
90
72
|
end
|
91
73
|
|
@@ -109,13 +91,7 @@ module Test
|
|
109
91
|
output("Finished in #{elapsed_time} seconds.")
|
110
92
|
nl
|
111
93
|
output(@result, result_color)
|
112
|
-
|
113
|
-
if n_tests.zero?
|
114
|
-
pass_percentage = 0
|
115
|
-
else
|
116
|
-
pass_percentage = 100.0 * (@n_successes / n_tests.to_f)
|
117
|
-
end
|
118
|
-
output("%g%% passed" % pass_percentage, result_color)
|
94
|
+
output("%g%% passed" % @result.pass_percentage, result_color)
|
119
95
|
end
|
120
96
|
|
121
97
|
def output_fault(fault)
|
@@ -159,7 +135,7 @@ module Test
|
|
159
135
|
def output_fault_message(fault)
|
160
136
|
output(fault.user_message) if fault.user_message
|
161
137
|
output_single("<")
|
162
|
-
output_single(fault.inspected_expected, color("
|
138
|
+
output_single(fault.inspected_expected, color("pass"))
|
163
139
|
output("> expected but was")
|
164
140
|
output_single("<")
|
165
141
|
output_single(fault.inspected_actual, color("failure"))
|
@@ -195,8 +171,7 @@ module Test
|
|
195
171
|
|
196
172
|
def test_finished(name)
|
197
173
|
unless @already_outputted
|
198
|
-
|
199
|
-
output_progress(".", color("success"))
|
174
|
+
output_progress(".", color("pass"))
|
200
175
|
end
|
201
176
|
@already_outputted = false
|
202
177
|
|
@@ -272,7 +247,10 @@ module Test
|
|
272
247
|
end
|
273
248
|
|
274
249
|
def color(name)
|
275
|
-
@color_scheme[name]
|
250
|
+
_color = @color_scheme[name]
|
251
|
+
_color ||= @color_scheme["success"] if name == "pass"
|
252
|
+
_color ||= ColorScheme.default[name]
|
253
|
+
_color
|
276
254
|
end
|
277
255
|
|
278
256
|
def fault_color(fault)
|
@@ -280,21 +258,7 @@ module Test
|
|
280
258
|
end
|
281
259
|
|
282
260
|
def result_color
|
283
|
-
|
284
|
-
if @result.pending_count > 0
|
285
|
-
color("pending")
|
286
|
-
elsif @result.omission_count > 0
|
287
|
-
color("omission")
|
288
|
-
elsif @result.notification_count > 0
|
289
|
-
color("notification")
|
290
|
-
else
|
291
|
-
color("success")
|
292
|
-
end
|
293
|
-
elsif @result.error_count > 0
|
294
|
-
color("error")
|
295
|
-
elsif @result.failure_count > 0
|
296
|
-
color("failure")
|
297
|
-
end
|
261
|
+
color(@result.status)
|
298
262
|
end
|
299
263
|
|
300
264
|
def guess_color_availability
|