726 lines
27 KiB
Ruby
726 lines
27 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative 'test_helper'
|
|
require 'pty'
|
|
require_relative '../lib/rdb'
|
|
|
|
class SelectionTest < RdbCase
|
|
def test_row_numeric_condition
|
|
out = rdb('row', 'Cost > 5', file: 'inventory')
|
|
assert_equal %w[3 6 7], data(out).map(&:first)
|
|
end
|
|
|
|
def test_row_regex_and_or
|
|
out = rdb('row', 'Description ~ /burner/ || Amount < 10', file: 'inventory')
|
|
assert_equal %w[1 3 6 7], data(out).map(&:first)
|
|
end
|
|
|
|
def test_select_is_row
|
|
assert_equal rdb('row', 'Cost > 5', file: 'inventory'), rdb('select', 'Cost > 5', file: 'inventory')
|
|
end
|
|
|
|
def test_column_reorders_and_adds_empty
|
|
out = rdb('column', 'Cost', 'Nope', 'Item', file: 'inventory')
|
|
assert_equal %w[Cost Nope Item], out.lines.first.chomp.split("\t")
|
|
assert_equal ['5.00', '', '1'], data(out).first
|
|
end
|
|
|
|
def test_column_without_arguments_is_identity
|
|
assert_equal read('inventory'), rdb('column', file: 'inventory')
|
|
end
|
|
|
|
def test_compute_sprintf
|
|
out = rdb('compute', 'Value = sprintf("%5.2f", Cost * Amount)', file: 'inventory')
|
|
assert_equal '1312.75', data(out)[5][3].strip
|
|
end
|
|
|
|
def test_sorttable_numeric_reverse
|
|
out = rdb('sorttable', '-nr', 'Cost', file: 'inventory')
|
|
assert_equal 'scales', data(out).first.last
|
|
end
|
|
|
|
def test_sorttable_two_keys
|
|
out = rdb('sorttable', 'Account', 'Date', file: 'journal')
|
|
assert_equal %w[101 101 101], data(out).first(3).map { |r| r[1] }
|
|
end
|
|
|
|
def test_record
|
|
assert_equal ['3', '5', '8.00', '40.00', 'clamps'], data(rdb('record', '3', file: 'inventory')).first
|
|
end
|
|
|
|
def test_number
|
|
out = rdb('number', file: 'inventory')
|
|
assert_equal %w[number Item], out.lines.first.split("\t").first(2).map(&:chomp)
|
|
assert_equal '7', data(out).last.first
|
|
end
|
|
|
|
def test_rename_addcol_headoff_headon
|
|
assert_equal 'Qty', rdb('rename', 'Amount', 'Qty', file: 'inventory').lines.first.split("\t")[1]
|
|
assert_equal 'Extra', rdb('addcol', 'Extra', file: 'inventory').lines.first.chomp.split("\t").last
|
|
assert_equal 7, rdb('headoff', file: 'inventory').lines.size
|
|
assert_equal %w[a b], rdb('headon', 'a', 'b', stdin: "1\t2\n").lines.first.chomp.split("\t")
|
|
end
|
|
end
|
|
|
|
class SetsTest < RdbCase
|
|
def test_union_intersect_difference
|
|
assert_equal 6, data(rdb('union', 'carexpense', 'expense')).size
|
|
assert_equal [%w[820113 101.62 car v car\ repairs], ['820114', '81.80', 'insur', 'c', 'car insurance allstate']],
|
|
data(rdb('intersect', 'carexpense', 'expense'))
|
|
assert_equal ['meal'], data(rdb('difference', 'expense', 'carexpense')).map { |r| r[2] }
|
|
end
|
|
|
|
def test_union_with_stdin_dash
|
|
out = rdb('union', '-', 'carexpense', file: 'expense')
|
|
assert_equal '820107', data(out).first.first
|
|
end
|
|
|
|
def test_jointable_natural_and_outer
|
|
write('owners', "Item\tOwner\n----\t-----\n1\tann\n3\tbob\n9\tzed\n")
|
|
inner = data(rdb('jointable', 'inventory', 'owners'))
|
|
assert_equal %w[ann bob], inner.map(&:last)
|
|
left = data(rdb('jointable', '-a1', 'inventory', 'owners'))
|
|
assert_equal 7, left.size
|
|
right = data(rdb('jointable', '-a2', 'inventory', 'owners'))
|
|
assert_equal 'zed', right.last.last
|
|
end
|
|
|
|
def test_paste_and_split
|
|
out = rdb('paste.rdb', 'expense', 'carexpense')
|
|
assert_equal 10, out.lines.first.split("\t").size
|
|
rdb('splittable', '-4', 'inventory')
|
|
assert_equal 4, data(read('inventoryaa')).size
|
|
assert_equal 3, data(read('inventoryab')).size
|
|
end
|
|
end
|
|
|
|
class SummaryTest < RdbCase
|
|
def test_total_mean_max_min
|
|
assert_equal '324', data(rdb('total', 'Amount', file: 'inventory')).first[1]
|
|
assert_equal '175.00', data(rdb('maximum', 'Cost', file: 'inventory')).first[2]
|
|
assert_equal '0.50', data(rdb('minimum', 'Cost', file: 'inventory')).first[2]
|
|
assert_equal '46.29', data(rdb('mean', 'Amount', file: 'inventory')).first[1]
|
|
end
|
|
|
|
def test_datatype_width_precision_widest
|
|
assert_equal %w[0 0 2 2 -1], data(rdb('datatype', file: 'inventory')).first
|
|
assert_equal '14', data(rdb('width', 'Description', file: 'inventory')).first.last
|
|
assert_equal '2', data(rdb('precision', 'Cost', file: 'inventory')).first[2]
|
|
assert_equal %w[4 6 6 7 14], data(rdb('widest', file: 'inventory')).first
|
|
end
|
|
|
|
def test_total_l_prints_table_too
|
|
out = rdb('total', '-l', 'Amount', file: 'inventory')
|
|
assert_equal 11, out.lines.size
|
|
end
|
|
|
|
def test_subtotal
|
|
out = data(rdb('subtotal', 'Date', 'Debit', 'Credit', file: 'journal'))
|
|
assert_equal [%w[861222 25000 25000], %w[861223 10000 10000], %w[861224 30000 30000]],
|
|
out.map { |r| [r[0], r[2], r[3]] }
|
|
end
|
|
|
|
def test_schema_dbdict
|
|
out = data(rdb('schema', 'inventory'))
|
|
assert_equal %w[inventory 5 Description], out.last
|
|
assert_equal %w[5 Description], data(rdb('dbdict', file: 'inventory')).last
|
|
end
|
|
|
|
def test_fd
|
|
assert_equal "true\n", rdb('fd', 'Item', 'Cost', file: 'inventory')
|
|
out, _e, st = run3('fd', 'Amount', 'Cost', stdin: read('inventory'))
|
|
assert_equal "false\n", out
|
|
refute st.success?
|
|
end
|
|
end
|
|
|
|
class FormatTest < RdbCase
|
|
def test_justify_aligns_decimals
|
|
out = data(rdb('justify', file: 'inventory'))
|
|
assert_equal ' 14.75', out[5][2]
|
|
assert_equal '175.00', out[6][2]
|
|
assert_equal ' 1', out[0][0]
|
|
end
|
|
|
|
def test_compresss_pad_rmblank_blank
|
|
assert_equal data(read('inventory').then { |s| rdb('compresss', stdin: rdb('justify', stdin: s)) }),
|
|
data(read('inventory'))
|
|
assert_equal 'rubber gloves' + ' ' * 70, data(rdb('pad', file: 'inventory')).first.last
|
|
t = "A\tB\n-\t-\n1\t2\n \t \n3\t4\n"
|
|
assert_equal 2, data(rdb('rmblank', stdin: t)).size
|
|
assert_equal [' '], data(rdb('blank', stdin: "A\n-\nab\n")).flatten
|
|
end
|
|
|
|
def test_trim_squeezes_columns
|
|
out = rdb('trim', file: 'inventory').lines
|
|
assert_equal 'Item Amount Cost Value Description ', out.first.chomp
|
|
end
|
|
|
|
def test_case_commands
|
|
assert_equal "Rubber Gloves\n", rdb('cap', stdin: "rubber gloves\n")
|
|
assert_equal "BIG WORDS\n", rdb('uppercase', 'big', 'words')
|
|
assert_equal "big words\n", rdb('lowercase', 'BIG', 'WORDS')
|
|
assert_equal "here is the good stuff\n", rdb('trimblank', stdin: " here is the good stuff \n")
|
|
end
|
|
|
|
def test_padstring_length_ascii_chr
|
|
assert_equal " string\n", rdb('padstring', '10', 'string')
|
|
assert_equal "string \n", rdb('padstring', '-10', 'string')
|
|
assert_equal "11\n", rdb('length', 'word1 word2')
|
|
assert_equal "97 65 49 \n", rdb('ascii', 'aA1')
|
|
assert_equal "aA1\n", rdb('chr', '97', '65', '49')
|
|
end
|
|
|
|
def test_see_shows_tabs_and_ends
|
|
assert_equal "a^Ib$\n", rdb('see', stdin: "a\tb\n")
|
|
end
|
|
|
|
def test_insertdash
|
|
assert_equal "A\tBB\n-\t--\n1\t2\n", rdb('insertdash', stdin: "A\tBB\n1\t2\n")
|
|
end
|
|
|
|
def test_word
|
|
assert_equal "I\nlove\nyou\n", rdb('word', stdin: "I love you love\n")
|
|
end
|
|
end
|
|
|
|
class ConvertTest < RdbCase
|
|
def test_list_round_trip
|
|
assert_equal read('inventory'), rdb('listtotable', stdin: rdb('tabletolist', file: 'inventory'))
|
|
end
|
|
|
|
def test_commands_accept_lists
|
|
out = rdb('column', 'Name', 'City', file: 'maillist')
|
|
assert out.start_with?("\n"), 'a list in gives a list out'
|
|
assert_includes out, "City\tMemphis"
|
|
assert_includes rdb('row', 'Number == 2', file: 'maillist'), 'Chiquita'
|
|
end
|
|
|
|
def test_listtosh
|
|
out = rdb('listtosh', stdin: "\nA\tx y\nB\tit's\n")
|
|
assert_equal "A='x y'\nB='it'\\''s'\n", out
|
|
end
|
|
|
|
def test_tabletofix_and_back
|
|
fixed = rdb('tabletofix', 'Item=3r', 'Description=14', file: 'inventory')
|
|
assert_equal " 1rubber gloves \n", fixed.lines.first
|
|
t = rdb('fixtotable', 'Item=1,3', 'Description=4,14', stdin: fixed)
|
|
assert_equal ['1', 'rubber gloves'], data(t).first
|
|
end
|
|
|
|
def test_tabletostruct
|
|
out = rdb('tabletostruct', 'Table', 'table', stdin: "A\tB\tC\n-\t-\t-\n1\t2\t3\n")
|
|
assert_includes out, "struct Table\n{\nchar *A;"
|
|
assert_includes out, '{ "1","2","3" }'
|
|
end
|
|
|
|
def test_prolog_and_translation_formats
|
|
assert_equal "parent(Sally,Fred).\nparent(Mike,Fred).\nparent(Fred,Jane).\nfemale(Jane).\nfemale(Sally).\n",
|
|
rdb('tabletofact', 'parent', 'female')
|
|
assert_equal "mother(X,Y) :- female(X), parent(X,Y).\n", rdb('tabletorule', 'ruletable')
|
|
assert_equal "define(I,Ich)\ndefine(love,liebe)\ndefine(you,dich)\n", rdb('tabletom4', file: 'ed.t')
|
|
assert_equal "s/I/Ich/g\ns/love/liebe/g\ns/you/dich/g\n", rdb('tabletosed', file: 'ed.t')
|
|
end
|
|
|
|
def test_translate
|
|
write('ed', rdb('tabletom4', file: 'ed.t'))
|
|
assert_equal "Ich liebe dich.\n", rdb('translate', 'ed', stdin: "I love you.\n")
|
|
end
|
|
|
|
def test_tabletotbl
|
|
assert_includes rdb('tabletotbl', file: 'inventory'), ".TS\nn n n n l."
|
|
end
|
|
|
|
def test_dates
|
|
assert_equal "890515\n", rdb('computedate', '890331', '45')
|
|
assert_equal "890201\n", rdb('computedate', '890132', '0')
|
|
assert_equal '1752827', data(rdb('julian', 'Date', file: 'journal')).first.first
|
|
assert_equal '861222', data(rdb('gregorian', 'Date', stdin: rdb('julian', 'Date', file: 'journal'))).first.first
|
|
assert_equal '22/12/86', data(rdb('gregorian', '-e', 'Date', stdin: rdb('julian', 'Date', file: 'journal'))).first.first
|
|
assert_match(/\A\d{6}\n\z/, rdb('todaysdate'))
|
|
end
|
|
|
|
def test_soundex_and_like
|
|
assert_equal 'A352', data(rdb('soundex', stdin: "Name\n----\nAdams\n")).first[1]
|
|
assert_equal 'R163', data(rdb('soundex', stdin: "Name\n----\nRobert\n")).first[1]
|
|
write('name.x', rdb('soundex', file: 'name'))
|
|
assert_equal %w[Mann Moon], data(rdb('like', 'Mann', 'name')).flatten.uniq.sort
|
|
end
|
|
|
|
def test_hashkey
|
|
assert_equal "2\n", rdb('hashkey', '11', 'ABCD')
|
|
assert_equal "4\n", rdb('hashkey', '29', 'abcdefg')
|
|
end
|
|
end
|
|
|
|
class FastAccessTest < RdbCase
|
|
# Same rows come back whichever method finds them.
|
|
%w[b h i s].each do |m|
|
|
define_method("test_search_method_#{m}") do
|
|
rdb('index', "-m#{m}", 'inventory', 'Description') unless m == 's'
|
|
out = rdb('search', "-m#{m}", 'inventory', 'Description', stdin: "scales\nclamps\n")
|
|
assert_equal %w[scales clamps].sort, data(out).map(&:last).sort
|
|
end
|
|
end
|
|
|
|
def test_record_number_method
|
|
rdb('index', '-mr', 'inventory')
|
|
assert_equal 'clamps', data(rdb('search', '-mr', 'inventory', stdin: "3\n")).first.last
|
|
end
|
|
|
|
def test_partial_match
|
|
rdb('index', '-mi', '-x', 'inventory', 'Description')
|
|
out = rdb('search', '-mi', '-x', 'inventory', 'Description', stdin: "cl\n")
|
|
assert_equal %w[clamps cleaning\ cloth], data(out).map(&:last).sort
|
|
end
|
|
|
|
def test_keys_from_a_table_and_no_head
|
|
out = rdb('search', '-h', 'inventory', 'Description', stdin: "Description\n-----------\nscales\n")
|
|
assert_equal "7\t5\t175.00\t875.00\tscales\n", out
|
|
end
|
|
|
|
def test_seek_reports_offsets
|
|
out = rdb('seek', 'inventory', 'Item', stdin: "5\n")
|
|
from, to = out.split.map(&:to_i)
|
|
assert_equal "5\t99\t2.45\t242.55\tcleaning cloth\n", read('inventory').byteslice(from, to - from)
|
|
end
|
|
|
|
def test_delete_blanks_in_place_and_keeps_offsets
|
|
size = File.size(File.join(@dir, 'inventory'))
|
|
rdb('delete', 'inventory', 'Item', stdin: "4\n")
|
|
assert_equal size, File.size(File.join(@dir, 'inventory'))
|
|
assert_equal '', read('inventory').lines[5].delete("\t \n")
|
|
assert_equal 6, data(rdb('rmblank', file: 'inventory')).size
|
|
end
|
|
|
|
%w[r i h].each do |m|
|
|
define_method("test_delete_updates_index_#{m}") do
|
|
rdb('index', "-m#{m}", 'inventory', 'Item')
|
|
rdb('delete', "-m#{m}", 'inventory', 'Item', stdin: "2\n")
|
|
key = m == 'r' ? "2\n" : "2\n"
|
|
assert_equal '', rdb('search', "-m#{m}", '-h', 'inventory', 'Item', stdin: key) if m != 'r'
|
|
assert_equal 3, data(rdb('search', "-m#{m}", 'inventory', 'Item', stdin: "3\n")).first.first.to_i if m != 'r'
|
|
end
|
|
end
|
|
|
|
def test_append_updates_each_index
|
|
%w[i h r].each do |m|
|
|
rdb('index', "-m#{m}", 'inventory', 'Item')
|
|
rdb('append', '-h', "-m#{m}", 'inventory', 'Item', stdin: "#{8 + %w[i h r].index(m)}\t1\t1.00\t1.00\tnew #{m}\n")
|
|
end
|
|
assert_equal 'new i', data(rdb('search', '-mi', 'inventory', 'Item', stdin: "8\n")).first.last
|
|
assert_equal 'new h', data(rdb('search', '-mh', 'inventory', 'Item', stdin: "9\n")).first.last
|
|
assert_equal 'new r', data(rdb('search', '-mr', 'inventory', stdin: "10\n")).first.last
|
|
end
|
|
|
|
def test_append_with_head_line
|
|
rdb('append', 'inventory', stdin: "Item\tAmount\tCost\tValue\tDescription\n----\t------\t----\t-----\t-----------\n8\t1\t1\t1\tnew\n")
|
|
assert_equal 8, data(read('inventory')).size
|
|
end
|
|
|
|
def test_replace_in_place_pads_last_column
|
|
out = rdb('seek', 'inventory', 'Item', stdin: "3\n")
|
|
from, to = out.split.map(&:to_i)
|
|
size = File.size(File.join(@dir, 'inventory'))
|
|
rdb('replace', 'inventory', from.to_s, to.to_s, stdin: "3\t6\t8.00\t48.00\tclamp\n")
|
|
assert_equal size, File.size(File.join(@dir, 'inventory'))
|
|
assert_equal '6', data(rdb('search', 'inventory', 'Item', stdin: "3\n")).first[1]
|
|
end
|
|
|
|
def test_replace_too_big_moves_record_to_end_and_fixes_index
|
|
rdb('index', '-mi', 'inventory', 'Item')
|
|
from, to, xf, xt = rdb('seek', '-mi', 'inventory', 'Item', stdin: "3\n").split.map(&:to_i)
|
|
big = "3\t5\t8.00\t40.00\t#{'a very long description ' * 3}\n"
|
|
rdb('replace', '-mi', 'inventory', from.to_s, to.to_s, xf.to_s, xt.to_s, stdin: big)
|
|
hit = rdb('search', '-mi', 'inventory', 'Item', stdin: "3\n")
|
|
assert_includes hit, 'a very long description'
|
|
assert_equal 1, data(hit).size
|
|
assert_equal 'inventory', 'inventory' # the blanked old slot stays, so offsets of other rows are unchanged
|
|
end
|
|
|
|
def test_replace_with_a_no_append_flag_fails_when_too_big
|
|
from, to = rdb('seek', 'inventory', 'Item', stdin: "3\n").split.map(&:to_i)
|
|
_o, _e, st = run3('replace', '-a', 'inventory', from.to_s, to.to_s, stdin: "3\t5\t8\t40\t#{'x' * 80}\n")
|
|
refute st.success?
|
|
end
|
|
|
|
def test_lock_and_unlock
|
|
_o, _e, st = run3('lock', 'inventory', '111', '10', '20', '0', '0')
|
|
assert st.success?
|
|
_o, e, st = run3('lock', 'inventory', '222', '15', '25', '0', '0')
|
|
refute st.success?
|
|
assert_match(/locked by process 111/, e)
|
|
assert run3('lock', 'inventory', '222', '30', '40', '0', '0').last.success?
|
|
run3('unlock', 'inventory', '111', '10', '20')
|
|
run3('unlock', 'inventory', '222', '30', '40')
|
|
assert run3('lock', 'inventory', '333', '15', '25', '0', '0').last.success?
|
|
run3('unlock', 'inventory', '333', '15', '25')
|
|
end
|
|
|
|
def test_enter_appends_rows
|
|
out = rdb('enter', '-1', 'inventory', stdin: "8\n2\n3\n4\nnew thing\n")
|
|
assert_includes out, 'Item: '
|
|
assert_equal 'new thing', data(read('inventory')).last.last
|
|
end
|
|
end
|
|
|
|
class CheckingTest < RdbCase
|
|
def test_check_rdb_finds_short_rows
|
|
_o, e, st = run3('check.rdb', stdin: "A\tB\n-\t-\n1\t2\n3\n")
|
|
refute st.success?
|
|
assert_match(/Columns \(1\) do not equal headline columns \(2\)/, e)
|
|
assert run3('check.rdb', stdin: read('inventory')).last.success?
|
|
end
|
|
|
|
def test_domain
|
|
out = rdb('domain', 'colors', stdin: rdb('column', 'Colors', file: 'orders'))
|
|
assert_equal %w[farble yellow], data(out).flatten
|
|
assert_equal "x\n", rdb('domain', 'colors', 'black', 'x')
|
|
end
|
|
|
|
def test_validate_prints_message_before_bad_row
|
|
out = rdb('validate', 'Onhand < 0 {print "negative Onhand in line " NR}', file: 'badinventory')
|
|
assert_includes out, "negative Onhand in line 3\n3\t-5"
|
|
assert_equal 5, out.scan(/rubber|test tubes|clamps|plates|cleaning/).size
|
|
end
|
|
|
|
def test_searchtree
|
|
write('tree', "Parent\tChild\n------\t-----\nSally\tFred\nFred\tJane\n")
|
|
assert_equal "TRUE\n", rdb('searchtree', 'tree', 'Sally', 'Jane', 'Parent', 'Child')
|
|
_o, _e, st = run3('searchtree', 'tree', 'Jane', 'Sally', 'Parent', 'Child')
|
|
refute st.success?
|
|
end
|
|
|
|
def test_not
|
|
assert_equal 255, run3('not', 'true').last.exitstatus
|
|
assert run3('not', 'false').last.success?
|
|
end
|
|
end
|
|
|
|
class ReportTest < RdbCase
|
|
def test_report_fills_form
|
|
out = rdb('report', 'form', file: 'maillist')
|
|
assert_includes out, "Ronald McDonald\nMcDonalds\n123 Mac Attack\nMemphis, TENN 30000\nHi Ronald McDonald:"
|
|
assert_includes out, 'Hi Chiquita Banana:'
|
|
end
|
|
|
|
def test_report_runs_commands_and_leaves_unknown_names
|
|
write('f2', "<Name>|<!echo <City>!>|<Nope>\n")
|
|
assert_equal "Ronald McDonald|Memphis\n|<Nope>\nChiquita Banana|San Jose\n|<Nope>\n", rdb('report', 'f2', file: 'maillist')
|
|
end
|
|
|
|
def test_label
|
|
assert_equal "Ronald McDonald\nMcDonalds\n123 Mac Attack\nMemphis, TENN 30000\n", rdb('label', file: 'maillist').lines.first(4).join
|
|
end
|
|
|
|
def test_screen_generates_a_script
|
|
out = rdb('screen', stdin: "Name : <Name>\nCity : <City>\n")
|
|
assert_includes out, 'cursor 1 9 ; read Name;'
|
|
assert_includes out, 'cursor 2 9 ; read City;'
|
|
assert_includes out, 'echo "$Name' + "\t" + '$City"'
|
|
end
|
|
|
|
def test_reportwriter_pages
|
|
out = rdb('reportwriter', '-3', 'Prices', file: 'inventory')
|
|
assert_equal 3, out.scan('Page').size
|
|
assert_includes out, 'Report Date:'
|
|
end
|
|
end
|
|
|
|
class HelpTest < RdbCase
|
|
def test_whatis_whatwill_commands
|
|
assert_includes rdb('whatis', 'column'), " column - display columns of a table in any order\n column column [ Column ... ] < tableorlist\n"
|
|
assert_includes rdb('whatwill', 'total'), 'subtotal'
|
|
assert_operator data(rdb('commands')).size, :>, 120
|
|
assert_operator rdb('howmany').to_i, :>, 120
|
|
end
|
|
|
|
def test_rdb_lists_commands_and_man_renders
|
|
assert_includes rdb('rdb'), 'jointable'
|
|
page = rdb('rdb', 'man', 'addcol')
|
|
assert_includes page, 'Add a column to a table'
|
|
assert_includes page, 'EXAMPLE'
|
|
end
|
|
|
|
def test_termput_and_cursor
|
|
assert_equal "\e[H\e[2J", rdb('termput', 'cl')
|
|
assert_equal "\e[3;8H", rdb('cursor', '3', '8')
|
|
end
|
|
|
|
def test_testall_mentions_every_command
|
|
script = File.read(File.join(DEMO, 'testall.cmds'))
|
|
names = data(rdb('commands')).map(&:first)
|
|
missing = names.reject { |n| script.match?(/(^|[\s|;(])#{Regexp.escape(n)}(\s|$)/) }
|
|
assert_empty missing, "testall.cmds lacks an example for: #{missing.join(' ')}"
|
|
end
|
|
|
|
def test_testall_runs_demo_script
|
|
out = rdb('testall')
|
|
assert_includes out, '$ row'
|
|
end
|
|
end
|
|
|
|
class ActTest < RdbCase
|
|
def test_tax_foot_cashflow_explode_bom
|
|
assert_equal " 3369\n", rdb('tax', '20000', file: 'X83')
|
|
assert_equal %w[10001 2], data(rdb('explode', '10001', '2', 'parttable')).first
|
|
assert_equal '52', data(rdb('explode', '10001', '2', 'parttable')).last.last
|
|
assert_equal '1062', data(rdb('cashflow', file: 'cash'))[2][2]
|
|
end
|
|
|
|
def test_adjust_balance_close
|
|
write('trialbalance', "Account\tDebit\tCredit\tFormat\tTaxline\tShort\tName\n-------\t-----\t------\t------\t-------\t-----\t----\n1010\t11178\t\tB-A\t4L01\tcash\tCash\n3010\t\t13500\tI-I\t101a\tsales\tSales\n")
|
|
write('journaladjust', "Account\tDebit\tCredit\tFormat\tTaxline\tName\n-------\t-----\t------\t------\t-------\t----\n1010\t100\t\tB-A\t4L01\tCash\n")
|
|
rdb('adjust')
|
|
assert_equal %w[1010 11278], data(read('adjustedtrial')).first.first(2)
|
|
rdb('balance')
|
|
assert_includes read('balancesheet'), 'Total Assets'
|
|
rdb('close')
|
|
assert_includes read('journalnext'), 'brought forward'
|
|
end
|
|
|
|
def test_chartdup
|
|
write('chart', "Account\tShort\n-------\t-----\n1\ta\n1\tb\n2\ta\n")
|
|
_o, err, st = run3('chartdup')
|
|
out, = run3('chartdup')
|
|
assert_includes out, 'Duplicate Account numbers in chart'
|
|
assert_includes out, 'Duplicate Short names in chart'
|
|
assert st.success?, err
|
|
end
|
|
|
|
ICASE = { 'RDB_ICASE' => '1' }.freeze
|
|
|
|
def test_column_names_are_case_sensitive_by_default
|
|
_o, err, st = run3('column', 'cost', stdin: File.read(File.join(@dir, 'inventory')))
|
|
assert st.success?, err
|
|
assert_equal %w[cost], _o.lines.first.chomp.split("\t")
|
|
end
|
|
|
|
def test_icase_column_and_sorttable
|
|
out, err, st = run3('column', 'COST', 'item', stdin: File.read(File.join(@dir, 'inventory')), env: ICASE)
|
|
assert st.success?, err
|
|
assert_equal %w[Cost Item], out.lines.first.chomp.split("\t")
|
|
out, = run3('sorttable', '-nr', 'cost', stdin: File.read(File.join(@dir, 'inventory')), env: ICASE)
|
|
assert_equal 'scales', data(out).first.last
|
|
end
|
|
|
|
def test_icase_compute_and_awk_words
|
|
src = File.read(File.join(@dir, 'inventory'))
|
|
out, err, st = run3('compute', 'value = sprintf("%5.2f", cost * AMOUNT)', stdin: src, env: ICASE)
|
|
assert st.success?, err
|
|
assert_equal '1312.75', data(out)[5][3].strip
|
|
# a column called Length must not swallow the length() function
|
|
t = "Length\tName\n------\t----\n3\tab\n"
|
|
out, err, st = run3('compute', 'Length = length(name)', stdin: t, env: ICASE)
|
|
assert st.success?, err
|
|
assert_equal [%w[2 ab]], data(out)
|
|
end
|
|
|
|
def test_icase_ambiguous_column_is_an_error
|
|
t = "Name\tname\n----\t----\na\tb\n"
|
|
_o, err, st = run3('column', 'NAME', stdin: t, env: ICASE)
|
|
refute st.success?
|
|
assert_match(/ambiguous/, err)
|
|
out, = run3('column', 'name', stdin: t, env: ICASE)
|
|
assert_equal [%w[b]], data(out)
|
|
end
|
|
end
|
|
|
|
class MaskTest < RdbCase
|
|
def form(text, frame: false) = Rdb::Mask::Form.parse(text, frame: frame)
|
|
|
|
def type(session, *keys)
|
|
keys.each { |k| k.start_with?("\e") || k.size == 1 ? session.feed(k) : k.each_char { |c| session.feed(c) } }
|
|
end
|
|
|
|
def test_parse_fields_buttons_and_width
|
|
f = form("Name: <Name:10> < City >\n[ Save ] [Cancel]\n")
|
|
assert_equal %w[Name City], f.names
|
|
assert_equal [0, 6, 10], f.slots[0].to_a.first(3)
|
|
assert_equal 9, f.slots[1].width # the width of "< City >"
|
|
assert_equal %w[Save Cancel], f.buttons.map(&:label)
|
|
assert_equal ["Name: #{' ' * 10} #{' ' * 9}", '[ Save ] [Cancel]'], f.lines
|
|
end
|
|
|
|
def test_command_in_form_and_frame
|
|
f = form("<!echo hi!> <A>\n", frame: true)
|
|
assert_equal '│ hi │', f.lines[1]
|
|
assert_equal [1, 5], f.slots.first.to_a.first(2)
|
|
end
|
|
|
|
def test_typing_and_navigation
|
|
s = Rdb::Mask::Session.new(form("<A:5> <B:5> [ OK ] [ Cancel ]\n"))
|
|
type(s, 'one', "\t", 'two', "\x7f", "\r")
|
|
assert_equal({ 'A' => 'one', 'B' => 'tw' }, s.values)
|
|
assert_equal :button, s.kind
|
|
assert_equal :accept, s.feed("\r")
|
|
end
|
|
|
|
def test_cancel_by_button_and_escape
|
|
s = Rdb::Mask::Session.new(form("<A:5> [ Save ] [ Abbrechen ]\n"))
|
|
s.feed("\e[Z")
|
|
assert_equal :cancel, s.feed(' ')
|
|
assert_equal :cancel, Rdb::Mask::Session.new(form("<A>\n")).feed("\e")
|
|
end
|
|
|
|
def test_enter_on_last_field_accepts_without_buttons
|
|
s = Rdb::Mask::Session.new(form("<A:3> <B:3>\n"))
|
|
assert_nil s.feed("\r")
|
|
assert_equal :accept, s.feed("\r")
|
|
end
|
|
|
|
def test_same_column_twice_is_one_field
|
|
s = Rdb::Mask::Session.new(form("<A:3> <A:3>\n"))
|
|
type(s, 'xy')
|
|
assert_equal({ 'A' => 'xy' }, s.values)
|
|
assert_equal %w[A], form("<A:3> <A:3>\n").names
|
|
end
|
|
|
|
def test_show_prints_picture_without_terminal
|
|
write('f', "Name: <Name:6>\n[ Save ] [ Cancel ]\n")
|
|
out = rdb('mask', '-s', file: 'f')
|
|
assert_includes out, "Name: ______\n[ Save ] [ Cancel ]"
|
|
assert_includes out, " Name\trow 1 col 7 width 6"
|
|
assert_includes out, " [Cancel]\trow 2 col 10 (cancels)"
|
|
assert_equal out, rdb('mask', '-s', 'f') # a file name works too
|
|
end
|
|
|
|
# Drive mask on a pseudo terminal; its table goes to a file, like in a pipe.
|
|
def run_on_pty(*keys)
|
|
out = File.join(@dir, 'mask.out')
|
|
result = nil
|
|
PTY.spawn({ 'PATH' => "#{BIN}:#{ENV['PATH']}" }, 'sh', '-c', "cd #{@dir} && mask < f > mask.out") do |r, w, pid|
|
|
sleep 0.5
|
|
keys.each do |k|
|
|
w.write k
|
|
sleep 0.1
|
|
r.read_nonblock(65_536) rescue nil
|
|
end
|
|
sleep 0.3
|
|
r.read_nonblock(65_536) rescue nil
|
|
_, st = Process.wait2(pid)
|
|
result = [File.exist?(out) ? File.read(out) : '', st.exitstatus]
|
|
end
|
|
result
|
|
end
|
|
|
|
def test_mask_on_a_terminal_writes_a_table
|
|
write('f', "Name: <Name:10>\nCity: <City:10>\n[ Save ] [ Cancel ]\n")
|
|
table, status = run_on_pty("Ann\r", "Rome\r", "\r")
|
|
assert_equal 0, status
|
|
assert_equal "Name\tCity\n----\t----\nAnn\tRome\n", table
|
|
end
|
|
|
|
def test_mask_cancel_writes_nothing
|
|
write('f', "Name: <Name:10>\n[ Save ] [ Cancel ]\n")
|
|
table, status = run_on_pty("Ann", "\e[Z", "\r")
|
|
assert_equal 1, status
|
|
assert_equal '', table
|
|
end
|
|
end
|
|
|
|
class PickTest < RdbCase
|
|
def session(multi: true, height: 3)
|
|
t = Rdb::Table.read(File.open(File.join(DEMO, 'inventory')))
|
|
Rdb::Pick::Session.new(t.head, t.rows, multi: multi).tap { |x| x.resize(height, 80) }
|
|
end
|
|
|
|
def keys(sess, words) = Rdb::Pick.script(words).map { |k| sess.feed(k) }.compact.last
|
|
|
|
def test_mark_and_select
|
|
s = session
|
|
assert_equal :accept, keys(s, 'down space down down space enter')
|
|
assert_equal %w[2 5], s.selected.map(&:first)
|
|
end
|
|
|
|
def test_single_mode_takes_the_cursor_row_and_ignores_space
|
|
s = session(multi: false)
|
|
assert_equal :accept, keys(s, 'down down space enter')
|
|
assert_equal %w[3], s.selected.map(&:first)
|
|
end
|
|
|
|
def test_scrolling_keeps_the_cursor_in_view
|
|
s = session(height: 3)
|
|
keys(s, 'down down down down')
|
|
assert_equal [4, 2], [s.cursor, s.top]
|
|
keys(s, 'end')
|
|
assert_equal [6, 4], [s.cursor, s.top]
|
|
keys(s, 'pgup')
|
|
assert_equal 3, s.cursor
|
|
keys(s, 'home')
|
|
assert_equal [0, 0], [s.cursor, s.top]
|
|
end
|
|
|
|
def test_filter_and_clear
|
|
s = session
|
|
keys(s, '/clam')
|
|
assert_predicate s, :typing?
|
|
keys(s, 'enter')
|
|
assert_equal [2], s.visible
|
|
keys(s, '/ esc')
|
|
assert_equal 7, s.visible.size
|
|
end
|
|
|
|
def test_marks_survive_a_filter_and_a_all_toggles
|
|
s = session
|
|
keys(s, 'space /scales enter')
|
|
assert_equal [0], s.marked
|
|
keys(s, 'a')
|
|
assert_equal [0, 6], s.marked.sort
|
|
keys(s, 'a')
|
|
assert_equal [0], s.marked
|
|
end
|
|
|
|
def test_cancel_and_empty_choice
|
|
assert_equal :cancel, keys(session, 'esc')
|
|
assert_equal :cancel, keys(session, '/zzz enter enter')
|
|
end
|
|
|
|
def test_command_with_keys_script_is_a_filter
|
|
out = rdb('pick', '-m', '-k', 'down space down down space enter', file: 'inventory')
|
|
assert_equal %w[2 5], data(out).map(&:first)
|
|
assert_equal '292.55', data(rdb('total', 'Value', stdin: out)).first[3]
|
|
end
|
|
|
|
def test_command_cancelled_exits_1_with_no_output
|
|
out, _err, st = run3('pick', '-k', 'esc', stdin: File.read(File.join(@dir, 'inventory')))
|
|
refute st.success?
|
|
assert_equal '', out
|
|
end
|
|
|
|
def test_command_reads_a_list
|
|
out = rdb('pick', '-k', 'down enter', stdin: rdb('tabletolist', file: 'owners'))
|
|
assert_equal [%w[3 bob]], data(out)
|
|
end
|
|
|
|
def run_on_pty(*key_strokes)
|
|
out = File.join(@dir, 'pick.out')
|
|
result = nil
|
|
PTY.spawn({ 'PATH' => "#{BIN}:#{ENV['PATH']}" }, 'sh', '-c', "cd #{@dir} && pick -m < inventory > pick.out") do |r, w, pid|
|
|
sleep 0.5
|
|
key_strokes.each do |k|
|
|
w.write k
|
|
sleep 0.1
|
|
r.read_nonblock(65_536) rescue nil
|
|
end
|
|
sleep 0.3
|
|
r.read_nonblock(65_536) rescue nil
|
|
_, st = Process.wait2(pid)
|
|
result = [File.exist?(out) ? File.read(out) : '', st.exitstatus]
|
|
end
|
|
result
|
|
end
|
|
|
|
def test_pick_on_a_terminal
|
|
table, status = run_on_pty("\e[B", ' ', "\e[B", ' ', "\r")
|
|
assert_equal 0, status
|
|
assert_equal %w[2 4], data(table).map(&:first)
|
|
end
|
|
|
|
def test_pick_on_a_terminal_cancel
|
|
table, status = run_on_pty("\e[B", "\e")
|
|
assert_equal 1, status
|
|
assert_equal '', table
|
|
end
|
|
end
|