Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3714bd5
Fixes for array data source tests.
corranwebster Dec 17, 2014
d9ba227
Use unitest2.
corranwebster Dec 17, 2014
8bfa702
More robust checking of get_bounds()
corranwebster Dec 17, 2014
6edd34e
Fix bare except statement.
corranwebster Dec 17, 2014
262c2bc
Add image data tests, minor fixes for array data tests.
corranwebster Dec 17, 2014
ffa0e9e
Replace unittest with unittest2
corranwebster Dec 17, 2014
5e16075
Add image reading tests for ImageData.
corranwebster Dec 17, 2014
bee330b
Ensure libpng is installed on Travis.
corranwebster Dec 17, 2014
e7c0551
...and zlib for travis.
corranwebster Dec 17, 2014
1f182e2
try again with Travis
corranwebster Dec 17, 2014
ddd8c40
Install PIL via apt get
corranwebster Dec 17, 2014
6eeb229
Yet one more attempt to get PIL to includ PNG support.
corranwebster Dec 17, 2014
2d90823
Add tests for function data source.
corranwebster Dec 17, 2014
530f2bb
Test range changes.
corranwebster Dec 17, 2014
0479ba4
Add tests for metadata.
corranwebster Dec 17, 2014
b7f87fb
Modernise/improve grid data source tests.
corranwebster Dec 17, 2014
60aed7c
Add multi array data source test case.
corranwebster Dec 17, 2014
93772da
Tests around serialization methods and reverse maps.
corranwebster Dec 18, 2014
9897383
Update CHANGES.txt.
corranwebster Dec 18, 2014
f25cb9f
Flake8.
corranwebster Dec 18, 2014
bfb0883
Remove set literal from test for 2.6 comptibility.
corranwebster Dec 18, 2014
2b25fda
Add setup to array data source.
corranwebster Dec 19, 2014
d743dd9
Add setUp method to multiarray data source test case.
corranwebster Dec 23, 2014
bc02978
Improvements based on suggestions in PR.
corranwebster Dec 23, 2014
6ba47ec
Fix typo.
corranwebster Dec 23, 2014
1a998ed
Merge branch 'master' into enh/data-source-tests
corranwebster Jan 6, 2015
d734ef3
Merge branch 'master' into enh/data-source-tests
corranwebster Jan 8, 2015
99bea26
Added more details to the CHANGES.txt
Jan 18, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ python:
before_install:
- sudo apt-get update
- sudo apt-get install python-numpy swig
# Simlinks for PIL compilation
- sudo ln -s /usr/lib/`uname -i`-linux-gnu/libfreetype.so /usr/lib/
- sudo ln -s /usr/lib/`uname -i`-linux-gnu/libjpeg.so /usr/lib/
- sudo ln -s /usr/lib/`uname -i`-linux-gnu/libpng.so /usr/lib/
- sudo ln -s /usr/lib/`uname -i`-linux-gnu/libz.so /usr/lib/
- source .travis_before_install
install:
- pip install cython
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Change summary since 4.5.0

New features/Improvements

* More comprehensive testing for AbstractDataSource subclasses. That
includes ArrayDataSource, FunctionDataSource, GridDataSource, ImageData,
MultiArrayDataSource (PR #244).

* Replaced chaco.base.bin_search by numpy.searchsorted-based routine for
5x speedup and remove use of zip in chaco.base.arg_find_runs in favour of
column_stack for 10x speedup in bad cases. (PR #263)
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
include chaco/*.h
include chaco/tests/data/PngSuite/*.png
include chaco/tests/data/PngSuite/LICENSE.txt
include chaco/tools/toolbars/images*.png
include chaco/tools/toolbars/images*.svg
include chaco/tools/toolbars/images*.txt
Expand Down
2 changes: 1 addition & 1 deletion chaco/array_data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def _compute_bounds(self, data=None):
data_len = 0
try:
data_len = len(data)
except:
except Exception:
pass
if data_len == 0:
self._min_index = 0
Expand Down
260 changes: 228 additions & 32 deletions chaco/tests/arraydatasource_test_case.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,148 @@
"""
Test of basic dataseries behavior.
Tests of ArrayDataSource behavior.
"""

import unittest
import pickle

from numpy import arange, array, allclose, empty, isnan, nan
import unittest2 as unittest
from numpy import arange, array, allclose, empty, isnan, nan, ones
from numpy.testing import assert_array_equal
import numpy as np

from chaco.api import ArrayDataSource, PointDataSource
from traits.testing.unittest_tools import UnittestTools


class ArrayDataTestCase(unittest.TestCase):
def test_basic_set_get(self):
myarray = arange(10)
sd = ArrayDataSource(myarray)
self.assertTrue(allclose(myarray, sd._data))
self.assert_(sd.value_dimension == "scalar")
return
class ArrayDataSourceTestCase(UnittestTools, unittest.TestCase):

def setUp(self):
self.myarray = arange(10)
self.mymask = array([i % 2 for i in self.myarray], dtype=bool)
self.data_source = ArrayDataSource(self.myarray)

def test_init_defaults(self):
data_source = ArrayDataSource()
assert_array_equal(data_source._data, [])
self.assertEqual(data_source.value_dimension, "scalar")
self.assertEqual(data_source.index_dimension, "scalar")
self.assertEqual(data_source.sort_order, "none")
self.assertFalse(data_source.is_masked())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing a few defaults: metadata, persist_data, index_dimension. Any reason not to add them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata is tested in its own unit test. I'll add the others.

self.assertEqual(data_source.persist_data, True)

def test_basic_setup(self):
assert_array_equal(self.myarray, self.data_source._data)
self.assertEqual(self.data_source.value_dimension, "scalar")
self.assertEqual(self.data_source.sort_order, "none")
self.assertFalse(self.data_source.is_masked())

def test_set_data(self):
new_array = arange(0, 20, 2)

with self.assertTraitChanges(self.data_source, 'data_changed',
count=1):
self.data_source.set_data(new_array)

assert_array_equal(new_array, self.data_source._data)
self.assertEqual(self.data_source.get_bounds(), (0, 18))
self.assertEqual(self.data_source.sort_order, "none")

def test_set_data_ordered(self):
new_array = arange(20, 0, -2)

with self.assertTraitChanges(self.data_source, 'data_changed',
count=1):
self.data_source.set_data(new_array, sort_order='descending')

assert_array_equal(new_array, self.data_source._data)
self.assertEqual(self.data_source.get_bounds(), (2, 20))
self.assertEqual(self.data_source.sort_order, "descending")

def test_set_mask(self):
with self.assertTraitChanges(self.data_source, 'data_changed',
count=1):
self.data_source.set_mask(self.mymask)

assert_array_equal(self.myarray, self.data_source._data)
assert_array_equal(self.mymask, self.data_source._cached_mask)
self.assertTrue(self.data_source.is_masked())
self.assertEqual(self.data_source.get_bounds(), (0, 9))

def test_remove_mask(self):
self.data_source.set_mask(self.mymask)
self.assertTrue(self.data_source.is_masked())

with self.assertTraitChanges(self.data_source, 'data_changed',
count=1):
self.data_source.remove_mask()

assert_array_equal(self.myarray, self.data_source._data)
self.assertIsNone(self.data_source._cached_mask, None)
self.assertFalse(self.data_source.is_masked())
self.assertEqual(self.data_source.get_bounds(), (0, 9))

def test_get_data(self):
assert_array_equal(self.myarray, self.data_source.get_data())

def test_get_data_no_data(self):
data_source = ArrayDataSource(None)

assert_array_equal(data_source.get_data(), 0.0)

def test_get_data_mask(self):
self.data_source.set_mask(self.mymask)

data, mask = self.data_source.get_data_mask()
assert_array_equal(data, self.myarray)
assert_array_equal(mask, self.mymask)

@unittest.skip('get_data_mask() fails in this case')
def test_get_data_mask_no_data(self):
data_source = ArrayDataSource(None)

data, mask = data_source.get_data_mask()
assert_array_equal(data, 0.0)
assert_array_equal(mask, True)

def test_get_data_mask_no_mask(self):
data, mask = self.data_source.get_data_mask()
assert_array_equal(data, self.myarray)
assert_array_equal(mask, ones(shape=10, dtype=bool))

def test_bounds(self):
# ascending
myarray = arange(10)
sd = ArrayDataSource(myarray, sort_order="ascending")
bounds = sd.get_bounds()
self.assert_(bounds == (0,9))
bounds = self.data_source.get_bounds()
self.assertEqual(bounds, (0, 9))

# descending
myarray = arange(10)[::-1]
sd = ArrayDataSource(myarray, sort_order="descending")
bounds = sd.get_bounds()
self.assert_(bounds == (0,9))
data_source = ArrayDataSource(myarray, sort_order="descending")
bounds = data_source.get_bounds()
self.assertEqual(bounds, (0, 9))

# no order
myarray = array([12,3,0,9,2,18,3])
sd = ArrayDataSource(myarray, sort_order="none")
bounds = sd.get_bounds()
self.assert_(bounds == (0,18))
return
myarray = array([12, 3, 0, 9, 2, 18, 3])
data_source = ArrayDataSource(myarray, sort_order="none")
bounds = data_source.get_bounds()
self.assertEqual(bounds, (0, 18))

def test_data_size(self):
# We know that ScalarData always returns the exact length of its data
myarray = arange(913)
sd = ArrayDataSource(myarray)
self.assert_(len(myarray) == sd.get_size())
return
def test_bounds_length_one(self):
# this is special-cased in the code, so exercise the code path
data_source = ArrayDataSource(array([1.0]))
bounds = data_source.get_bounds()
self.assertEqual(bounds, (1.0, 1.0))

def test_bounds_length_zero(self):
# this is special-cased in the code, so exercise the code path
data_source = ArrayDataSource(array([]))
bounds = data_source.get_bounds()
# XXX this is sort of inconsistent with test_bounds_all_nans()
self.assertEqual(bounds, (0, 0))

def test_bounds_empty(self):
data_source = ArrayDataSource()
bounds = data_source.get_bounds()
# XXX this is sort of inconsistent with test_bounds_all_nans()
self.assertEqual(bounds, (0, 0))

def test_bounds_all_nans(self):
myarray = empty(10)
Expand All @@ -53,12 +152,108 @@ def test_bounds_all_nans(self):
self.assertTrue(isnan(bounds[0]))
self.assertTrue(isnan(bounds[1]))

def test_bounds_some_nan(self):
data_source = ArrayDataSource(array([np.nan, 3, 0, 9, np.nan, 18, 3]))
bounds = data_source.get_bounds()
self.assertEqual(bounds, (0, 18))

def test_bounds_negative_inf(self):
data_source = ArrayDataSource(array([12, 3, -np.inf, 9, 2, 18, 3]))
bounds = data_source.get_bounds()
self.assertEqual(bounds, (-np.inf, 18))

def test_bounds_positive_inf(self):
data_source = ArrayDataSource(array([12, 3, 0, 9, 2, np.inf, 3]))
bounds = data_source.get_bounds()
self.assertEqual(bounds, (0, np.inf))

def test_bounds_negative_positive_inf(self):
data_source = ArrayDataSource(array([12, 3, -np.inf, 9, 2, np.inf, 3]))
bounds = data_source.get_bounds()
self.assertEqual(bounds, (-np.inf, np.inf))

def test_bounds_non_numeric(self):
myarray = np.array([u'abc', u'foo', u'bar', u'def'], dtype=unicode)
sd = ArrayDataSource(myarray)
bounds = sd.get_bounds()
data_source = ArrayDataSource(myarray)
bounds = data_source.get_bounds()
self.assertEqual(bounds, (u'abc', u'def'))

def test_data_size(self):
# We know that ArrayDataTestCase always returns the exact length of
# its data
myarray = arange(913)
data_source = ArrayDataSource(myarray)
self.assertEqual(len(myarray), data_source.get_size())

def test_reverse_map(self):
# sort_order ascending
myarray = arange(10)
data_source = ArrayDataSource(myarray, sort_order='ascending')

self.assertEqual(data_source.reverse_map(4.0), 4)

# sort_order descending
myarray = arange(10)[::-1]
data_source = ArrayDataSource(myarray, sort_order='descending')

self.assertEqual(data_source.reverse_map(4.0), 5)

# sort_order none
myarray = array([12, 3, 0, 9, 2, 18, 3])
data_source = ArrayDataSource(myarray, sort_order='none')

with self.assertRaises(NotImplementedError):
data_source.reverse_map(3)

def test_metadata(self):
self.assertEqual(self.data_source.metadata,
{'annotations': [], 'selections': []})

def test_metadata_changed(self):
with self.assertTraitChanges(self.data_source, 'metadata_changed',
count=1):
self.data_source.metadata = {'new_metadata': True}

def test_metadata_items_changed(self):
with self.assertTraitChanges(self.data_source, 'metadata_changed',
count=1):
self.data_source.metadata['new_metadata'] = True

def test_serialization_state(self):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add a test that calls __getstate__ with self.persist_data = False. That code is currently never exercised.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have that, but it is skipped because of brokenness with the super calls: https://github.com/enthought/chaco/blob/enh/data-source-tests/chaco/tests/arraydatasource_test_case.py#L252

state = self.data_source.__getstate__()
self.assertNotIn('value_dimension', state)
self.assertNotIn('index_dimension', state)
self.assertNotIn('persist_data', state)

@unittest.skip("persist_data probably shouldn't be persisted")
def test_serialization_state_no_persist(self):
self.data_source.persist_data = False

state = self.data_source.__getstate__()
self.assertNotIn('value_dimension', state)
self.assertNotIn('index_dimension', state)
self.assertNotIn('persist_data', state)
for key in ["_data", "_cached_mask", "_cached_bounds", "_min_index",
"_max_index"]:
self.assertIn(key, state)

@unittest.skip("I think this is just broken")
def test_serialization_post_load(self):
self.data_source.set_mask(self.mymask)

pickled_data_source = pickle.dumps(self.data_source)
unpickled_data_source = pickle.loads(pickled_data_source)
unpickled_data_source._post_load()

self.assertEqual(unpickled_data_source._cached_bounds, ())
self.assertEqual(unpickled_data_source._cached_mask, None)

assert_array_equal(self.data_source.get_data(),
unpickled_data_source.get_data())

mask = unpickled_data_source.get_data_mask()[1]
assert_array_equal(mask, ones(10))


class PointDataTestCase(unittest.TestCase):
# Since PointData is mostly the same as ScalarData, the key things to
Expand All @@ -69,16 +264,17 @@ def create_array(self):
def test_basic_set_get(self):
myarray = self.create_array()
pd = PointDataSource(myarray)
self.assertTrue(allclose(myarray,pd._data))
self.assertTrue(allclose(myarray, pd._data))
self.assert_(pd.value_dimension == "point")
return

def test_bounds(self):
myarray = self.create_array()
pd = PointDataSource(myarray)
self.assertEqual(pd.get_bounds(),((0,0), (9,90)))
self.assertEqual(pd.get_bounds(), ((0, 0), (9, 90)))
return


if __name__ == '__main__':
import nose
nose.run()
8 changes: 8 additions & 0 deletions chaco/tests/data/PngSuite/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
PngSuite
--------

Permission to use, copy, modify and distribute these images for any
purpose and without fee is hereby granted.


(c) Willem van Schaik, 1996, 2011
Binary file added chaco/tests/data/PngSuite/basi6a08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added chaco/tests/data/PngSuite/basn2c08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading