TabViewer / tabview

Python curses command line CSV and tabular data viewer
Other
458 stars 49 forks source link

Add unit tests and continuous integration #73

Closed scls19fr closed 9 years ago

scls19fr commented 9 years ago

Hello,

if think it could be a good idea to add unit tests (nosetests are great) and continuous integration using Travis. It will ensure that modification of code will not break something for both Python 2.7 and 3.3.

But first, sample directory with sample data should be present (see my PR https://github.com/firecat53/tabview/pull/71 )

We need to create a tests directory.

We should put a test_tabview.py file inside with

#!/usr/bin/env python
# encoding: utf-8

import tabview as t

def test_tabview_file():
    r = t.view(fn='sample/data_ohlcv.csv', loop=False)
    assert(r is None) # maybe view should return something else

def test_tabview_data():
    a = [["a","b","c"], ["d","e","f"]]
    r = t.view(a, loop=False)
    assert(r is None)

You will probably notice the "loop" parameter

we need to change

def view(data=None, fn=None, enc=None):
    ...
        while loop:

to

def view(data=None, fn=None, enc=None, loop=True):
    ...
        while loop:

So we can run unittest using

$ nosetests -s -v

It will run viewer and exit (because loop is False)

Adding Travis-CI is not difficult too.

We just need a .travis.yml file with

language: python
python:
  - "2.7"
  - "3.3"

# command to install dependencies
install:
  - "pip install ."
  - "pip install -r requirements-test.txt"

# command to run tests
script: nosetests

and a requirements-test.txt file with

pytest
nose
#and probably other packages

A maintener need to log in to Travis. Sync projects list and toggle build for "tabview" project Only mainteners can do this.

I did this for 2 of my previous project https://github.com/scls19fr/pandas_degreedays https://github.com/scls19fr/openweathermap_requests

I hope it helps

Kind regards

firecat53 commented 9 years ago

Yep, a much needed part of the project! Bear with me...testing is one of the things I've been subconsciously avoiding for a long time now :) Thanks for the push...

scls19fr commented 9 years ago

I'm sure you was testing... but you was testing manually ;-)

After this we can proudly display a badge "build passing" !

scls19fr commented 9 years ago

I have commit these changes https://github.com/scls19fr/tabview/ but I can't send PR

I don't understand exactly why see https://github.com/scls19fr/tabview/compare/firecat53:master...master

That's maybe because of my previous PR https://github.com/firecat53/tabview/pull/71 about sample directory I'm sorry I'm not very confortable with Git, branches, ...

Build is passing fine https://travis-ci.org/scls19fr/tabview both for 2.7 and 3.3

You just need to reproduce this and result will be available at https://travis-ci.org/firecat53/tabview

firecat53 commented 9 years ago

I played with this a bit today, added some more sample CSV files and activated the Travis integration. Climbing up the learning curve....

The branch is named 'scls19fr-master'. I removed the 'loop' stuff you added to tabview.py, because it didn't actually test anything, but just stopped the loop immediately on startup. I added two tests so far: 1 to test detect_encoding() and the other to test process_file(). I'm very new to testing, so please tell me if the tests are sufficient or need more!

Looks like my data is a little off, because the python 2.7 build failed.

scls19fr commented 9 years ago

The goal of this loop flag is to ensure that view function is running fine and returns correctly for any python version (without any exception raised). I really think that you should keep that. It's also part of the testing process but I think that view should return a value (either return 0 or sys.exit(n)). It looks maybe strange but that's a very minimal test.

You can also do:

def test_tabview_file():
    r = t.view(fn='sample/data_ohlcv.csv', loop=False)
    assert(r==0)

def test_tabview_data():
    a = [["a","b","c"], ["d","e","f"]]
    r = t.view(a, loop=False)
    assert(r==0)

and add

return(0)

at the end of def view(...):

You are right to add some other tests about encoding but I don't understand why you are using

script: py.test

and not

script: nosetests

in you .travis.yml file but I'm not a test specialist.

nosetests is a test loader

I don't understand why your test with python 2.7 fails

Did you try to add flag -v to py.test or nosetests ?

Anyway it proves that your test is too restrictive.

What is strange is that I'm not able to reproduce this locally.

scls19fr commented 9 years ago

Oh! I just understand

u"Ladies' 7 oz. ComfortSoft\xae Cotton Piqu\xe9 Polo - WHITE - L" != "Ladies' 7 oz. ComfortSoft\xc2\xae Cotton Piqu\xc3\xa9 Polo - WHITE - L"

because

type(u"Ladies' 7 oz. ComfortSoft\xae Cotton Piqu\xe9 Polo - WHITE - L")
unicode

and

type("Ladies' 7 oz. ComfortSoft\xae Cotton Piqu\xe9 Polo - WHITE - L")
str

but I still don't understand why test is passing fine locally on my python 2.7 install !

firecat53 commented 9 years ago

I did a little research last night and chose py.test instead of nosetests for the testing framework.

Using the 'loop' function tests only this code:

if sys.version_info.major < 3:
    lc_all = locale.getlocale(locale.LC_ALL)
    locale.setlocale(locale.LC_ALL, '')
else:
    lc_all = None
try:
    while loop:
........

It doesn't check any of the process_file, detect_encoding, or any of the curses code, because the while loop won't execute at all if loop is False. I tried an alternative that would run once through the entire display sequence and then exit (to test the view() function), but it didn't work with the testing framework because there's no valid curses screen.

I tried looking for information on testing python curses projects and I couldn't find anything! We might just have to unittest the individual functions rather than being able to do a single big integration test. Maybe. ??

scls19fr commented 9 years ago

Oh yes I forget that it wasn't executed once ! sorry about that.

So you probably something like

     while True:
        try:
            if data is not None:
                d = data
            elif fn is not None:
                d = process_file(fn, enc)
            curses.wrapper(main, pad_data(d))
        except (QuitException, KeyboardInterrupt):
            return
        except ReloadException:
            #continue
            pass
        if not loop:
            return

I'm trying this locally but I need to press "q" to quit

Locally we should be able to make this work

Maybe when loop is set to false, we need a thread to close curses ?

Did you succeed testing curses locally or was it really a problem with Travis ?

scls19fr commented 9 years ago

you wrote (but I don't see it on github) "I still can't get an integration test to work with pytest and curses to at least check if the whole thing runs through once."

so the problem is not a Travis problem... if even locally it doesn't work

I noticed

curses.wrapper(main, d)

maybe the loop flag could be pass to main then to Viewer.__init__ then we could do self.loop = loop in Viewer class initialization

and run method could be modified like this

def run(self):
    # Clear the screen and display the menu of keys
    # Main loop:
    while True:
        self.display()
        self.handle_keys()
        if not self.loop:
            break
scls19fr commented 9 years ago

sorry... I just noticed you was working in scls19fr-master branch not in master ...

I see you mix unittest and py.test. Are you sure all the test being auto discovered ?

firecat53 commented 9 years ago

Finally got tests (a couple of integration tests and a couple of unit tests) working using the default unittest module. Py.test and nosetests are great, but they manipulate some of data such that getting the curses integration tests to pass was almost impossible.

scls19fr commented 9 years ago

ok great ! I think that's an important part of this project before implementing anything else.

Thanks

firecat53 commented 9 years ago

Thanks...it was painful! There's not much info about curses integration testing.

scls19fr commented 9 years ago

I wonder if py.test wasn't buggy with curses... maybe it could be worth to make a minimal test to show to py.test dev that it's working using nose, unittest but not py.test

Maybe you should also add a badge

.. image:: https://travis-ci.org/firecat53/tabview.svg?branch=master :target: https://travis-ci.org/firecat53/tabview

2015-01-17 21:23 GMT+01:00 Scott Hansen notifications@github.com:

Thanks...it was painful! There's not much info about curses integration testing.

— Reply to this email directly or view it on GitHub https://github.com/firecat53/tabview/issues/73#issuecomment-70382797.

scls19fr commented 9 years ago

I also noticed you are running test in .travis.yml file using:

script: python tests/test_tabview.py

there is a test discovery feature with classic unittest

$ unit2 discover tests -v

Maybe it could be a good idea to update .travis.yml accordingly