%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/vendor/oojs/oojs-ui/bin/ |
| Current File : //www/varak.net/wiki.varak.net/vendor/oojs/oojs-ui/bin/testsuitegenerator.rb |
require 'pp'
require_relative 'docparser'
if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help']
$stderr.puts "usage: ruby #{$PROGRAM_NAME} <dirA> <dirB> -o <file>"
$stderr.puts " ruby #{$PROGRAM_NAME} src php -o tests/JSPHP-suite.json"
else
if (i = ARGV.index('-o'))
out = File.open(ARGV[i + 1], 'wb')
ARGV[i..(i + 1)] = []
else
out = $stdout
end
dir_a, dir_b = ARGV
js = parse_any_path dir_a
php = parse_any_path dir_b
class_names = (js + php).map{|c| c[:name] }.sort.uniq
tests = {}
classes = php.select{|c| class_names.include? c[:name] }
# classes with different PHP and JS implementations.
# we can still compare the PHP-infuse result to JS result, though.
infuse_only_classes = %w[ComboBoxInputWidget
RadioSelectInputWidget CheckboxMultiselectInputWidget NumberInputWidget]
testable_classes = classes
.reject{|c| c[:abstract] } # can't test abstract classes
.reject{|c| !c[:parent] || c[:trait] || c[:parent] == 'Theme' } # can't test abstract
.reject{|c| %w[Element Widget Layout Theme].include? c[:name] } # no toplevel
make_class_instance_placeholder = lambda do |klass, config|
'_placeholder_' + {
class: klass,
config: config
}.to_json
end
make_htmlsnippet_placeholder = make_class_instance_placeholder.curry['HtmlSnippet']
# values to test for each type
expandos = {
'null' => [nil],
'int' => [0, -1, 300], # PHP code
'number' => [0, -1, 300], # JS code
'bool' => [true, false], # PHP code
'boolean' => [true, false], # JS code
'string' => ['Foo bar', '<b>HTML?</b>', '', ' '],
'HtmlSnippet' => ['Foo bar', '<b>HTML?</b>', ''].map(&make_htmlsnippet_placeholder),
}
# Values to test for specific config options, when not all values of given type are valid.
# Empty array will result in no tests for this config option being generated.
sensible_values = {
'align' => %w[top inline left],
'href' => ['http://example.com/'],
['TextInputWidget', 'type'] => %w[text number password foo],
['ButtonInputWidget', 'type'] => %w[button submit foo],
['NumberInputWidget', 'step'] => %w[1],
['NumberInputWidget', 'buttonStep'] => %w[2],
['NumberInputWidget', 'pageStep'] => %w[10],
['NumberInputWidget', 'min'] => %w[1 3],
['NumberInputWidget', 'max'] => %w[3 5],
['FieldLayout', 'errors'] => expandos['string'].map{|v| [v] }, # treat as string[]
['FieldLayout', 'notices'] => expandos['string'].map{|v| [v] }, # treat as string[]
'type' => %w[text button],
'method' => %w[GET POST],
'target' => ['_blank'],
'accessKey' => ['k'],
'tabIndex' => [-1, 0, 100, '42'],
'maxLength' => [100],
'icon' => ['image'],
'indicator' => ['down'],
'flags' => %w[progressive primary],
'progress' => [0, 50, 100, false],
'options' => [
[],
[ { 'data' => 'a', 'label' => 'A' } ],
[ { 'data' => 'a' }, { 'data' => 'b' } ],
[ { 'data' => 'a', 'label' => 'A' }, { 'data' => 'b', 'label' => 'B' } ],
],
'value' => ['', 'a', 'b', '<b>HTML?</b>'],
# deprecated, makes test logs spammy
'multiline' => [],
# usually makes no sense in JS
'autofocus' => [],
# too simple to test?
'action' => [],
'enctype' => [],
'name' => [],
# the dynamic 'clear' indicator in JS messes everything up
['SearchInputWidget', 'value'] => [],
['SearchInputWidget', 'indicator'] => [],
['SearchInputWidget', 'required'] => [],
['SearchInputWidget', 'disabled'] => [],
# these are defined by Element and would bloat the tests
'classes' => [],
'id' => [],
'content' => [],
'text' => [],
}
find_class = lambda do |klass|
return classes.find{|c| c[:name] == klass }
end
expand_types_to_values = lambda do |types|
# For abstract classes (not "testable"), test a few different subclasses instead
if types.delete 'Widget'
types.push 'ButtonWidget', 'TextInputWidget'
end
if types.delete 'InputWidget'
types.push 'CheckboxInputWidget', 'TextInputWidget'
end
return types.map{|t|
as_array = true if t.sub! '[]', ''
if expandos[t]
# Primitive. Run tests with the provided values.
vals = expandos[t]
elsif testable_classes.find{|c| c[:name] == t }
# OOUI object. Test suite will instantiate one and run the test with it.
constructor = find_class.call(t)[:methods].find{|m| m[:name] == '#constructor' }
params = constructor ? (constructor[:params] || []) : []
config = params.map{|config_option|
types = config_option[:type].split '|'
values = expand_types_to_values.call(types)
{ config_option[:name] => values[0] }
}
vals = [
make_class_instance_placeholder.call( t, config.inject({}, :merge) )
]
else
# We don't know how to test this. The empty value will result in no
# tests being generated for this combination of config values.
vals = []
end
as_array ? vals.map{|v| [v] } : vals
}.inject(:+)
end
find_config_sources = lambda do |klass_name|
return [] unless klass_name
klass_names = [klass_name]
while klass_name
klass = find_class.call(klass_name)
break unless klass
klass_names +=
find_config_sources.call(klass[:parent]) +
klass[:mixins].map(&find_config_sources).flatten
klass_name = klass[:parent]
end
return klass_names.uniq
end
testable_classes.each do |klass|
class_name = klass[:name]
tests[class_name] = {
infuseonly: !infuse_only_classes.index(class_name).nil?,
tests: [],
}
config_sources = find_config_sources.call(class_name)
.map{|c| find_class.call(c)[:methods].find{|m| m[:name] == '#constructor' } }
config = config_sources.compact.map{|c| c[:config] }.compact.inject([], :+)
constructor = klass[:methods].find{|m| m[:name] == '#constructor' }
required_config = constructor ? (constructor[:params] || []) : []
# generate every possible configuration of configuration option sets
maxlength = [config.length, 2].min
config_combinations = (0..maxlength).map{|l| config.combination(l).to_a }.inject(:+)
# for each set, generate all possible values to use based on option's type
config_combinations = config_combinations.map{|config_comb|
config_comb += required_config
expanded = config_comb.map{|config_option|
types = config_option[:type].split '|'
values =
sensible_values[ [ class_name, config_option[:name] ] ] ||
sensible_values[ config_option[:name] ] ||
expand_types_to_values.call(types)
values.map{|v| config_option.dup.merge(value: v) }
}
expanded.empty? ? [ [] ] : expanded[0].product(*expanded[1..-1])
}.inject(:concat).uniq
config_combinations.each do |config_comb|
tests[class_name][:tests] << {
class: class_name,
config: Hash[ config_comb.map{|c| [ c[:name], c[:value] ] } ]
}
end
end
$stderr.puts "Generated #{tests.values.map{|a| a[:tests].length}.inject(:+)} test cases."
$stderr.puts tests.map{|class_name, class_tests| "* #{class_name}: #{class_tests[:tests].length}" }
out.puts JSON.pretty_generate tests
end