The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# vim: set ts=2 sts=2 sw=2 expandtab smarttab:
use strict;
use warnings;
use Test::More 0.96;
use Test::MockObject 1.09 ();
use Symbol; # core
use lib 't/lib';
use CSVTester;

my $mod = $CSVTester::mod;

# get_raw_row()
test_with_all_csv_classes get_raw_row => sub {
  my $csv_class = shift;
  my $loader = new_ok($mod, [io => new_io(), default_column_type => 'foo', csv_class => $csv_class]);
  # columns determined from first row
  is_deeply($loader->columns, [[qw(fld1 foo)], [qw(fld2 foo)]], 'columns');
  is_deeply($loader->column_names, [qw(fld1 fld2)], 'column names');
  # get_row calls get_raw_row
  is_deeply($loader->get_raw_row, [qw(row1 col2)],     'raw row 1');
  is_deeply($loader->get_row,     [qw(row2 col2)],     'row 2');
  is_deeply($loader->get_raw_row, [qw(row3), "col 2"], 'raw row 3');
};

# default_name()
test_with_all_csv_classes default_name => sub {
  my $csv_class = shift;
  # we're basically testing File::Basename isn't necessary
  foreach my $test (
    ['/tmp/goober.csv' => 'goober'],
    ['har de har har.fudge.csv' => 'har de har har.fudge'],
    ['ahoy.tab' => 'ahoy'],
    ['' => 'csv'],
  ){
    my ($file, $exp) = @$test;
    is(new_ok($mod, [io => new_io(), file => $file, csv_class => $csv_class])->default_name, $exp, 'default_name');
  }
};

# prepare_data() options
test_with_all_csv_classes 'prepare_data() options' => sub {
  my $csv_class = shift;
  my $loader;
  my $mock = Test::MockObject->new();
  $mock->fake_module('Fake_CSV',
    new => sub { bless( ($_[1]||{}), $_[0] ) },
    getline => sub { [1] }
  );

  # csv
  $loader = new_ok($mod, [io => new_io(), csv_class => $csv_class]);
  isa_ok($loader->{csv}, $csv_class);
  my $csv = Fake_CSV->new({goo => 'ber'});
  $loader = new_ok($mod, [io => new_io(), csv => $csv]);
  is($loader->{csv}, $csv, 'csv option');
  is($loader->{csv}->{goo}, 'ber', 'csv option');

  # csv_class
  $loader = new_ok($mod, [io => new_io(), csv_class => $csv_class]);
  isa_ok($loader->{csv}, $csv_class);
  $loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV']);
  isa_ok($loader->{csv}, 'Fake_CSV');

  # csv_opts, csv_defaults
  $loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV']);
  is($loader->{csv}->{binary}, 1, 'csv_defaults');
  $loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV', csv_opts => {binary => 12}]);
  is($loader->{csv}->{binary}, 12, 'csv_defaults overridden with csv_opts');
  $loader = new_ok($mod, [io => new_io(), csv_class => 'Fake_CSV', csv_defaults => {}]);
  ok(!exists($loader->{csv}->{binary}), 'csv_defaults emptied');

  # file (no io)
  is(eval { $mod->new(file => '') }, undef, 'die w/o file');
  like($@, qr/Cannot proceed without/, 'no file error');
  is(eval { $mod->new(file => catfile('t', 'file that does', 'not.exist')) }, undef, 'die w/o file');
  like($@, qr/Failed to open/, 'cannot find file');

  $loader = eval { $mod->new(file => catfile(qw( t data example.csv ))) };
  is($@, '', 'no error');
  isa_ok($loader, $mod, 'file exists');

  # no_header
  $loader = new_ok($mod, [io => new_io()]);
  is_deeply($loader->column_names, [qw(fld1 fld2)], 'csv 1st row column names');
  is_deeply($loader->get_raw_row, [qw(row1 col2)],  'csv 2nd row is 1st row of data');

  $loader = new_ok($mod, [io => new_io(), columns => [qw(goo ber)]]);
  is_deeply($loader->column_names, [qw(goo ber)],   'provided column names');
  is_deeply($loader->get_raw_row, [qw(row1 col2)],  'csv 2nd row is 1st row of data');

  $loader = new_ok($mod, [io => new_io(), columns => [qw(goo ber)], no_header => 1]);
  is_deeply($loader->column_names, [qw(goo ber)],   'provided column names');
  is_deeply($loader->get_raw_row, [qw(fld1 fld2)],  'no_header option returns 1st row of csv in 2st row of data');
};

done_testing;

sub new_io {
  # use fake io to avoid opening files
  my $data = ["fld1,fld2\n", "row1,col2\n", "row2,col2\n", qq[row3,"col 2"\n]];
  return Test::MockObject->new(
    # Text::CSV_PP calls eof() which requires a Glob reference
    Symbol::gensym()
  )->mock(getline => sub { shift @$data });
}