#
# = test/unit/bio/io/flatfile/test_buffer.rb - unit test for Bio::FlatFile::BufferedInputStream
#
#   Copyright (C) 2006 Naohisa Goto <ng@bioruby.org>
#
# License:: The Ruby License
#
#  $Id:$
#

# loading helper routine for testing bioruby
require 'pathname'
load Pathname.new(File.join(File.dirname(__FILE__), ['..'] * 4,
                            'bioruby_test_helper.rb')).cleanpath.to_s

# libraries needed for the tests
require 'test/unit'
require 'stringio'
require 'bio/io/flatfile/buffer'

module Bio::TestFlatFileBufferedInputStream
  TestDataPath = BioRubyTestDataPath
  TestDataFastaFormat01 = File.join(TestDataPath, 'fasta', 'example1.txt')

  class TestBufferedInputStreamParseFileOpenArg < Test::Unit::TestCase
    K = Bio::FlatFile::BufferedInputStream

    def _parse_file_open_mode(mode)
      K.module_eval { _parse_file_open_mode(mode) }
    end
    private :_parse_file_open_mode

    def _parse_file_open_arg(*arg)
      K.module_eval { _parse_file_open_arg(*arg) }
    end
    private :_parse_file_open_arg

    def test_parse_file_open_mode_nil
      assert_equal(nil, _parse_file_open_mode(nil))
    end

    def test_parse_file_open_mode_integer
      assert_equal({ fmode_integer: 127 },
                   _parse_file_open_mode(127))
    end

    def test_parse_file_open_mode_str
      assert_equal({ fmode_string: 'r+b' },
                   _parse_file_open_mode('r+b'))
    end

    def test_parse_file_open_mode_str_with_ext_enc
      assert_equal({ fmode_string: 'r+t',
                     external_encoding: 'UTF-8' },
                   _parse_file_open_mode('r+t:UTF-8'))
    end

    def test_parse_file_open_mode_str_with_enc
      assert_equal({ fmode_string: 'rb',
                     external_encoding: 'EUC-JP',
                     internal_encoding: 'UTF-8' },
                   _parse_file_open_mode('rb:EUC-JP:UTF-8'))
    end

    def test_parse_file_open_arg_nil
      assert_equal({}, _parse_file_open_arg(nil))
    end

    def test_parse_file_open_arg_integer
      assert_equal({ fmode_integer: 127 },
                   _parse_file_open_arg(127))
    end

    def test_parse_file_open_arg_str
      assert_equal({ fmode_string: 'r+b' },
                   _parse_file_open_arg('r+b'))
    end

    def test_parse_file_open_arg_str_with_ext_enc
      assert_equal({ fmode_string: 'r+t',
                     external_encoding: 'UTF-8' },
                   _parse_file_open_arg('r+t:UTF-8'))
    end

    def test_parse_file_open_arg_str_with_enc
      assert_equal({ fmode_string: 'rb',
                     external_encoding: 'EUC-JP',
                     internal_encoding: 'UTF-8' },
                   _parse_file_open_arg('rb:EUC-JP:UTF-8'))
    end

    def test_parse_file_open_arg_str_perm
      assert_equal({ fmode_string: 'r+b',
                     perm: 0o644 },
                   _parse_file_open_arg('r+b', 0o644))
    end

    def test_parse_file_open_arg_int_perm
      assert_equal({ fmode_integer: 255,
                     perm: 0o755 },
                   _parse_file_open_arg(255, 0o755))
    end

    def test_parse_file_open_arg_int_perm_opt
      assert_equal({ fmode_integer: 191,
                     perm: 0o600,
                     textmode: true,
                     internal_encoding: 'EUC-JP' },
                   _parse_file_open_arg(191, 0o600,
                                        textmode: true,
                                        internal_encoding: 'EUC-JP'))
    end

    def test_parse_file_open_arg_int_opt
      assert_equal({ fmode_integer: 191,
                     textmode: true,
                     internal_encoding: 'EUC-JP' },
                   _parse_file_open_arg(191,
                                        textmode: true,
                                        internal_encoding: 'EUC-JP'))
    end

    def test_parse_file_open_arg_str_perm_opt
      assert_equal({ fmode_string: 'a',
                     perm: 0o644,
                     binmode: true,
                     external_encoding: 'UTF-8' },
                   _parse_file_open_arg('a', 0o644,
                                        binmode: true,
                                        external_encoding: 'UTF-8'))
    end

    def test_parse_file_open_arg_str_opt
      assert_equal({ fmode_string: 'a',
                     binmode: true,
                     external_encoding: 'UTF-8' },
                   _parse_file_open_arg('a',
                                        binmode: true,
                                        external_encoding: 'UTF-8'))
    end

    def test_parse_file_open_arg_opt
      assert_equal({ fmode_string: 'r',
                     binmode: true,
                     external_encoding: 'UTF-8' },
                   _parse_file_open_arg(mode: 'r',
                                        binmode: true,
                                        external_encoding: 'UTF-8'))
    end

    def test_parse_file_open_arg_opt_with_integer_mode
      assert_equal({ fmode_integer: 123,
                     perm: 0o600,
                     textmode: true,
                     external_encoding: 'EUC-JP' },
                   _parse_file_open_arg(mode: 123,
                                        perm: 0o600,
                                        textmode: true,
                                        external_encoding: 'EUC-JP'))
    end
  end # class TestBufferedInputStreamParseFileOpenArg

  class TestBufferedInputStreamClassMethod < Test::Unit::TestCase
    def test_self_for_io
      io = File.open(TestDataFastaFormat01)
      obj = Bio::FlatFile::BufferedInputStream.for_io(io)
      assert_instance_of(Bio::FlatFile::BufferedInputStream, obj)
      assert_equal(TestDataFastaFormat01, obj.path)
    end

    def test_self_open_file
      obj = Bio::FlatFile::BufferedInputStream.open_file(TestDataFastaFormat01)
      assert_instance_of(Bio::FlatFile::BufferedInputStream, obj)
      assert_equal(TestDataFastaFormat01, obj.path)
    end

    def test_self_open_file_with_block
      obj2 = nil
      Bio::FlatFile::BufferedInputStream.open_file(TestDataFastaFormat01) do |obj|
        assert_instance_of(Bio::FlatFile::BufferedInputStream, obj)
        assert_equal(TestDataFastaFormat01, obj.path)
        obj2 = obj
      end
      # Since Ruby 2.3.0, calling IO#close to closed IO object is allowed
      # without error.
      if RUBY_VERSION >= '2.3.0'
        assert_nothing_raised { obj2.close }
      else
        assert_raise(IOError) { obj2.close }
      end
    end
  end # class TestBufferedInputStreamClassMethod

  class TestBufferedInputStream < Test::Unit::TestCase
    def setup
      io = File.open(TestDataFastaFormat01)
      io.binmode
      path = TestDataFastaFormat01
      @obj = Bio::FlatFile::BufferedInputStream.new(io, path)
    end

    def test_to_io
      assert_kind_of(IO, @obj.to_io)
    end

    def test_close
      assert_nil(@obj.close)
    end

    def test_rewind
      @obj.prefetch_gets
      @obj.rewind
      assert_equal('', @obj.prefetch_buffer)
    end

    def test_pos
      @obj.gets
      @obj.gets
      @obj.prefetch_gets
      assert_equal(117, @obj.pos) # the number depends on original data
    end

    def test_pos=
      str = @obj.gets
      assert_equal(0, @obj.pos = 0)
      assert_equal(str, @obj.gets)
    end

    def test_eof_false_first
      assert_equal(false, @obj.eof?)
    end

    def test_eof_false_after_prefetch
      nil while @obj.prefetch_gets
      assert_equal(false, @obj.eof?)
    end

    def test_eof_true
      nil while @obj.gets
      assert_equal(true, @obj.eof?)
    end

    def test_gets
      @obj.gets
      @obj.gets
      assert_equal('gagcaaatcgaaaaggagagatttctgcatatcaagagaaaattcgagctgagatacattccaagtgtggctactc', @obj.gets.chomp)
    end

    def test_gets_equal_prefetch_gets
      @obj.prefetch_gets
      str = @obj.prefetch_gets
      @obj.prefetch_gets
      @obj.gets
      assert_equal(@obj.gets, str)
    end

    def test_gets_rs
      rs = 'tggtg'
      str = <<~__END_OF_STR__
        aggcactagaattgagcagtgaa
        gaagatgaggaagatgaagaagaagatgaggaagaaatcaagaaagaaaaatgcgaattttctgaagatgtagacc
        gatttatatggacggttgggcaggactatggtttggatgatctggtcgtgcggcgtgctctcgccaagtacctcga
        agtggatgtttcggacatattggaaagatacaatgaactcaagcttaagaatgatggaactgctggtg
      __END_OF_STR__
      @obj.gets(rs)
      @obj.gets(rs)
      assert_equal(str.chomp, @obj.gets(rs))
    end

    def test_gets_rs_equal_prefetch_gets
      rs = 'tggtg'
      @obj.prefetch_gets(rs)
      str = @obj.prefetch_gets(rs)
      @obj.prefetch_gets(rs)
      @obj.gets(rs)
      assert_equal(@obj.gets(rs), str)
    end

    def test_gets_rs_within_buffer
      rs = 'tggtg'
      a = []
      20.times { a.push @obj.gets }
      @obj.ungets(a.join(''))

      assert_equal(">At1g02580 mRNA (2291 bp) UTR's and CDS\naggcgagtggttaatggagaaggaaaaccatgaggacgatggtg",
                   @obj.gets(rs))

      assert_equal('ggctgaaagtgattctgtgattggtaagagacaaatctattatttgaatggtg',
                   @obj.gets(rs).split("\n")[-1])

      assert_equal('aggcactagaattgagcagtgaa',
                   @obj.gets(rs).split("\n")[0])

      assert_equal('aggcttct', @obj.gets(rs).split("\n")[0])

      assert_equal('agacacc', @obj.gets(rs).split("\n")[0])
    end

    def test_gets_paragraph_mode
      @obj.gets('')
      @obj.gets('')
      assert_equal('>At1g65300: mRNA 837bp (shortened at end)',
                   @obj.gets('').split("\n")[0])
    end

    def test_gets_paragraph_mode_equal_prefetch_gets
      rs = ''
      @obj.prefetch_gets(rs)
      str = @obj.prefetch_gets(rs)
      @obj.prefetch_gets(rs)
      @obj.gets(rs)
      assert_equal(@obj.gets(rs), str)
    end

    def test_gets_paragraph_mode_within_buffer
      @obj.gets('')
      a = []
      20.times { a.push @obj.gets }
      @obj.ungets(a.join(''))

      assert_equal('>At1g65300: mRNA 837bp',
                   @obj.gets('').split("\n")[0])

      assert_equal('>At1g65300: mRNA 837bp (shortened at end)',
                   @obj.gets('').split("\n")[0])

      assert_equal('>At1g65300: mRNA 837bp (shortened from start)',
                   @obj.gets('').split("\n")[0])
    end

    def test_ungets
      @obj.gets
      @obj.gets
      str1 = @obj.gets
      str2 = @obj.gets
      assert_nil(@obj.ungets(str2))
      assert_nil(@obj.ungets(str1))
      assert_equal(str1, @obj.gets)
      assert_equal(str2, @obj.gets)
    end

    def test_getc
      assert_equal('>', @obj.getc)
    end

    def test_getc_after_prefetch
      @obj.prefetch_gets
      assert_equal('>', @obj.getc)
    end

    def test_ungetc
      c = @obj.getc
      assert_nil(@obj.ungetc(c))
      assert_equal(c, @obj.getc)
    end

    def test_ungetc_after_prefetch
      str = @obj.prefetch_gets
      c = @obj.getc
      assert_nil(@obj.ungetc(c))
      assert_equal(str, @obj.gets)
    end

    def test_prefetch_buffer
      str = @obj.prefetch_gets
      str += @obj.prefetch_gets
      assert_equal(str, @obj.prefetch_buffer)
    end

    def test_prefetch_gets
      @obj.prefetch_gets
      @obj.prefetch_gets
      @obj.gets
      str = @obj.prefetch_gets
      @obj.gets
      assert_equal(str, @obj.gets)
    end

    def test_prefetch_gets_with_arg
      # test @obj.gets
      str = @obj.prefetch_gets("\n>")
      assert_equal(str, @obj.gets("\n>"))
      # test using IO object
      io = @obj.to_io
      io.rewind
      assert_equal(str, io.gets("\n>"))
    end

    def test_skip_spaces
      @obj.gets('CDS')
      assert_nil(@obj.skip_spaces)
      assert_equal('a', @obj.getc)
    end
  end # class TestBufferedInputStream
end # module Bio::TestFlatFile
