diff --git a/tools/nem_test_vectors.erb b/tools/nem_test_vectors.erb new file mode 100644 index 0000000000..0e7ed946ec --- /dev/null +++ b/tools/nem_test_vectors.erb @@ -0,0 +1,18 @@ +// test vectors from <%= source_url %> +START_TEST(<%= test_name %>) +{ + static const struct { +<% fields.each do |(name, type)| -%> + <%= if type.nil? then 'const char *' else "#{type} " end %><%= name %>; +<% end -%> + } tests[] = { +<% data.each do |values| -%> + { <% values.each do |value| %><%= value %>, <% end %>}, +<% end -%> + }; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + // TODO: Implement test + } +} +END_TEST diff --git a/tools/nem_test_vectors.rb b/tools/nem_test_vectors.rb new file mode 100755 index 0000000000..46586f84e0 --- /dev/null +++ b/tools/nem_test_vectors.rb @@ -0,0 +1,120 @@ +#!/usr/bin/env ruby +require 'highline' +require 'open-uri' + +TEMPLATE_NAME = (Pathname.new(__FILE__).sub_ext '.erb').to_s.freeze + +@terminal = HighLine.new($stdin, $stderr) + +def github_files + require 'octokit' + + @github_files ||= Octokit.contents('NemProject/nem-test-vectors') + .select do |file| + file.name.end_with? '.dat' + end +end + +def choose_data_file + @terminal.choose do |menu| + github_files.each do |file| + menu.choice(file.name) { file.download_url } + end + + menu.prompt = 'Which file? ' + + menu.index = :none + menu.select_by = :name + end +end + +def load_header(line) + line = line.dup + abort 'Header is not a comment' unless line.slice!(0) == '#' + + line.split(':').each(&:strip!) +end + +def parse_field_answer(answer) + if answer.empty? + nil + elsif /^(?:(?\w+) )?(?\w+)$/ =~ answer + [identifier, type] + else + raise NotValidQuestionError + end +end + +def ask_fields(header) + header.map do |name| + @terminal.ask "Field for `#{name}'? " do |question| + question.answer_type = lambda(&method(:parse_field_answer)) + end + end +end + +def load_data_line(line) + abort 'Line does not begin with colon' unless line.slice!(0) == ':' + + values = line.split(':').each(&:strip!) + values.pop if values.last.empty? + + values +end + +def load_data(file, count) + file.each_line.lazy.reject { |line| line.start_with? '#' } + .take(count) + .map(&method(:load_data_line)) + .to_a +end + +def remove_skipped_fields(fields, data) + data.each do |values| + abort 'Line does not match header' unless values.size == fields.size + + values.reject!.each_with_index { |_, index| fields[index].nil? } + end + + fields.compact! +end + +def format_data_fields(fields, data) + data.each do |values| + fields.each_with_index do |(_, type), index| + values[index] = values[index].dump if type.nil? + end + end +end + +def template(source_url, fields, data) + test_name = @terminal.ask('Name for test? ') do |question| + question.validate = /./ + end + + erb = ERB.new(File.read(TEMPLATE_NAME), nil, '-') + erb.filename = TEMPLATE_NAME + + erb.result(binding) +end + +download_url = choose_data_file + +source_code = open download_url do |file| + line = file.readline + + header = load_header(line) + @terminal.say line + + count = @terminal.ask('How many vectors to import? ', Integer) + + fields = ask_fields(header) + data = load_data(file, count) + + remove_skipped_fields(fields, data) + format_data_fields(fields, data) + + template(download_url, fields, data) +end + +puts source_code