Files
rdb/test/rdb_test.rb
T

519 lines
20 KiB
Ruby

# frozen_string_literal: true
require_relative 'test_helper'
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_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