# Copyright (c) 2002, 2003 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""
Test the DBFTable class
"""

__version__ = "$Revision: 1662 $"
# $Source$
# $Id: test_dbf_table.py 1662 2003-08-27 13:51:01Z bh $


import os
import unittest

import support
support.initthuban()

from Thuban.Model.table import DBFTable, MemoryTable, \
     FIELDTYPE_DOUBLE, FIELDTYPE_INT, FIELDTYPE_STRING, \
     table_to_dbf
import dbflib

class TestTableToDBF(unittest.TestCase, support.FileTestMixin):

    def test_table_to_dbf(self):
        """Test table_to_dbf"""
        memtable = MemoryTable([("type", FIELDTYPE_STRING),
                                ("value", FIELDTYPE_DOUBLE),
                                ("code", FIELDTYPE_INT)],
                               [("UNKNOWN", 0.0, 0),
                                ("Foo", 0.5, -1),
                                ("Foo", 1.0/256, 100),
                                ("bar", 1e10, 17)])

        filename = self.temp_file_name("test_table_to_dbf.dbf")
        table_to_dbf(memtable, filename)

        dbf = dbflib.DBFFile(filename)
        self.assertEquals(dbf.read_record(2),
                          {'code': 100, 'type': 'Foo', 'value': 0.00390625})
        self.assertEquals(dbf.field_count(), 3)
        self.assertEquals(dbf.record_count(), 4)
        self.assertEquals(dbf.field_info(0),
                          (dbflib.FTString, "type", 7, 0))
        self.assertEquals(dbf.field_info(1),
                          (dbflib.FTDouble, "value", 24, 12))
        self.assertEquals(dbf.field_info(2),
                          (dbflib.FTInteger, "code", 3, 0))

        # save selected rows
        table_to_dbf(memtable, filename, [1, 3])

        dbf = dbflib.DBFFile(filename)
        self.assertEquals(dbf.read_record(0),
                          {'code': -1, 'type': 'Foo', 'value': 0.5})
        self.assertEquals(dbf.field_count(), 3)
        self.assertEquals(dbf.record_count(), 2)
        self.assertEquals(dbf.field_info(0),
                          (dbflib.FTString, "type", 7, 0))
        self.assertEquals(dbf.field_info(1),
                          (dbflib.FTDouble, "value", 24, 12))
        self.assertEquals(dbf.field_info(2),
                          (dbflib.FTInteger, "code", 3, 0))

    def test_table_to_dbf_long_col_names(self):
        """Test table_to_dbf with long column names."""
        memtable = MemoryTable([("SOME_STRING", FIELDTYPE_STRING),
                                ("SOME_LONG_COLNAME", FIELDTYPE_DOUBLE),
                                ("SOME_LONG_COLNAME_2", FIELDTYPE_DOUBLE)],
                               [("UNKNOWN", 0.0, 0.0),
                                ("Foo", 1.0, 0.0),
                                ("Foo", 478.23482182999999, 0.0),
                                ("bar", -2.25, 1.0)])

        filename = self.temp_file_name("test_table_to_dbf_long_col_names.dbf")
        table_to_dbf(memtable, filename)

        dbf = dbflib.DBFFile(filename)
        self.assertEquals(dbf.read_record(3),
                          {'SOME_STRIN': "bar", 'SOME_LONG_': -2.25,
                           'SOME_LONG1': 1.0})

class TestDBFTable(unittest.TestCase, support.FileTestMixin):

    def setUp(self):
        """Create a new dbf file. The name is in self.filename"""
        simple = MemoryTable([("type", FIELDTYPE_STRING),
                              ("value", FIELDTYPE_DOUBLE),
                              ("code", FIELDTYPE_INT)],
                             [("UNKNOWN", 0.0, 0),
                              ("Foo", 0.5, -1),
                              ("Foo", 0.25, 100),
                              ("bar", 1e10, 17)])

        self.filename = self.temp_file_name("test_dbf_read.dbf")
        table_to_dbf(simple, self.filename)

    def test_num_rows(self):
        """Test DBFTable.NumRows()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.NumRows(), 4)

    def test_num_columns(self):
        """Test DBFTable.NumColumns()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.NumColumns(), 3)

    def test_columns(self):
        """Test DBFTable.Columns()"""
        table = DBFTable(self.filename)
        columns = table.Columns()
        self.assertEquals(columns[0].name, "type")
        self.assertEquals(columns[0].type, FIELDTYPE_STRING)
        self.assertEquals(columns[1].name, "value")
        self.assertEquals(columns[1].type, FIELDTYPE_DOUBLE)
        self.assertEquals(columns[2].name, "code")
        self.assertEquals(columns[2].type, FIELDTYPE_INT)

    def test_column(self):
        """Test DBFTable.Column()"""
        table = DBFTable(self.filename)
        # The Column method can be called with either an index or a name
        col = table.Column(2)
        self.assertEquals(col.name, "code")
        self.assertEquals(col.type, FIELDTYPE_INT)
        col = table.Column("value")
        self.assertEquals(col.name, "value")
        self.assertEquals(col.type, FIELDTYPE_DOUBLE)

    def test_has_column(self):
        """Test DBFTable.HasColumn()"""
        table = DBFTable(self.filename)
        # HasColumn
        self.failUnless(table.HasColumn("value"))
        self.failUnless(table.HasColumn(2))
        # HasColumn for non-exisiting columns
        self.failIf(table.HasColumn("non_existing_name"))
        self.failIf(table.HasColumn(100))

    def test_read_row_as_dict(self):
        """Test DBFTable.ReadRowAsDict()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.ReadRowAsDict(1),
                          {"type": "Foo", "value": 0.5, "code": -1})

    def test_read_row_as_dict_row_count_mode(self):
        """Test DBFTable.ReadRowAsDict() row count address mode"""
        table = DBFTable(self.filename)
        self.assertEquals(table.ReadRowAsDict(1, row_is_ordinal = 1),
                          {"type": "Foo", "value": 0.5, "code": -1})

    def test_read_value(self):
        """Test DBFTable.ReadValue()"""
        table = DBFTable(self.filename)
        # The column in ReadValue may be given as either name or index
        self.assertEquals(table.ReadValue(2, 0), "Foo")
        self.assertEquals(table.ReadValue(3, "code"), 17)

    def test_read_value_row_count_mode(self):
        """Test DBFTable.ReadValue() row count address mode"""
        table = DBFTable(self.filename)
        # The column in ReadValue may be given as either name or index
        self.assertEquals(table.ReadValue(2, 0, row_is_ordinal = 1), "Foo")
        self.assertEquals(table.ReadValue(3, "code", row_is_ordinal = 1), 17)

    def test_row_id_to_ordinal(self):
        """Test DBFTable.RowIdToOrdinal()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.RowIdToOrdinal(5), 5)

    def test_row_oridnal_to_id(self):
        """Test DBFTable.RowOrdinalToId()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.RowOrdinalToId(5), 5)

    def test_value_range(self):
        """Test DBFTable.ValueRange()"""
        table = DBFTable(self.filename)
        self.assertEquals(table.ValueRange("code"), (-1, 100))
        self.assertEquals(table.ValueRange(1), (0, 1e10))

    def test_unique_values(self):
        """Test DBFTable.UniqueValues()"""
        table = DBFTable(self.filename)

        # The column can be specified by name or index
        self.assertEquals(table.UniqueValues("type"),
                          ["Foo", "UNKNOWN", "bar"])
        self.assertEquals(table.UniqueValues(2), [-1, 0, 17, 100])

    def test_dependencies(self):
        """Test DBFTable.Dependencies()"""
        # A DBFTable doesn't have dependencies
        table = DBFTable(self.filename)
        self.assertEquals(len(table.Dependencies()), 0)

    def test_filename(self):
        """Test DBFTable.FileName()"""
        # A DBFTable doesn't have dependencies
        table = DBFTable(self.filename)
        self.assertEquals(table.FileName(), self.filename)

    def test_title(self):
        """Test DBFTable.Title()"""
        # A DBFTable doesn't have dependencies
        table = DBFTable(self.filename)
        self.assertEquals(table.Title(), "test_dbf_read")


class TestDBFTableWriting(unittest.TestCase, support.FileTestMixin):

    def test_write(self):
        """Test DBFTable.write_record()"""
        eq = self.assertEquals

        # First create a DBF file
        dbffilename = self.temp_file_name("dbftable_write.dbf")
        dbf = dbflib.create(dbffilename)
        dbf.add_field("NAME", dbflib.FTString, 20, 0)
        dbf.add_field("INT", dbflib.FTInteger, 10, 0)
        dbf.add_field("FLOAT", dbflib.FTDouble, 10, 4)
        dbf.write_record(0, {'NAME': "Weatherwax", "INT":1,
                             "FLOAT":3.1415926535})
        dbf.close()

        # Create the table
        table = DBFTable(dbffilename)
        record = table.ReadRowAsDict(0)
        # The FLOAT value is different from above because of rounding
        eq(record, {'NAME': "Weatherwax", "INT":1, "FLOAT":3.1415999999999999})

        # change only one field
        table.write_record(0, {"NAME": "Ogg"})
        # check whether it has been written immediately
        dbf = dbflib.DBFFile(dbffilename)
        control = dbf.read_record(0)
        eq(control, {'NAME': "Ogg", "INT":1, "FLOAT":3.1415999999999999})
        dbf.close()

        # check whether the table itself returns the new value
        eq(table.ReadRowAsDict(0),
           {'NAME': "Ogg", "INT":1, "FLOAT":3.1415999999999999})

        # Check whether we can specify the record as a tuple
        table.write_record(0, ("Garlick", 2, 1.5))
        eq(table.ReadRowAsDict(0), {"NAME": "Garlick", "INT": 2, "FLOAT": 1.5})

        table.Destroy()


if __name__ == "__main__":
    support.run_tests()
