~ubuntu-branches/ubuntu/saucy/python-happybase/saucy

« back to all changes in this revision

Viewing changes to tests/test_api.py

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2013-05-30 13:56:42 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130530135642-tveld2y1dbkhmuv3
Tags: 0.6-1
* New upstream release (Closes: #712971).
* Ran wrap-and-sort.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
"""
2
 
HappyBase API tests.
 
2
HappyBase tests.
3
3
"""
4
4
 
 
5
import collections
5
6
import os
6
 
import collections
7
 
 
8
 
from nose.tools import (assert_dict_equal,
9
 
                        assert_equal,
10
 
                        assert_false,
11
 
                        assert_in,
12
 
                        assert_is_instance,
13
 
                        assert_is_not_none,
14
 
                        assert_not_in,
15
 
                        assert_raises,
16
 
                        assert_true)
17
 
 
18
 
import happybase
 
7
import random
 
8
import threading
 
9
 
 
10
from nose.tools import (
 
11
    assert_dict_equal,
 
12
    assert_equal,
 
13
    assert_false,
 
14
    assert_in,
 
15
    assert_is_instance,
 
16
    assert_is_not_none,
 
17
    assert_not_in,
 
18
    assert_raises,
 
19
    assert_true,
 
20
)
 
21
 
 
22
from happybase import Connection, ConnectionPool, NoConnectionsAvailable
19
23
 
20
24
HAPPYBASE_HOST = os.environ.get('HAPPYBASE_HOST')
21
25
HAPPYBASE_PORT = os.environ.get('HAPPYBASE_PORT')
22
26
HAPPYBASE_COMPAT = os.environ.get('HAPPYBASE_COMPAT', '0.92')
 
27
HAPPYBASE_TRANSPORT = os.environ.get('HAPPYBASE_TRANSPORT', 'buffered')
23
28
KEEP_TABLE = ('HAPPYBASE_NO_CLEANUP' in os.environ)
24
29
 
25
30
TABLE_PREFIX = 'happybase_tests_tmp'
26
31
TEST_TABLE_NAME = 'test1'
27
32
 
 
33
connection_kwargs = dict(
 
34
    host=HAPPYBASE_HOST,
 
35
    port=HAPPYBASE_PORT,
 
36
    table_prefix=TABLE_PREFIX,
 
37
    compat=HAPPYBASE_COMPAT,
 
38
    transport=HAPPYBASE_TRANSPORT,
 
39
)
 
40
 
 
41
 
 
42
# Yuck, globals
28
43
connection = table = None
29
44
 
30
45
 
 
46
def maybe_delete_table():
 
47
    if KEEP_TABLE:
 
48
        return
 
49
 
 
50
    if TEST_TABLE_NAME in connection.tables():
 
51
        print "Test table already exists; removing it..."
 
52
        connection.delete_table(TEST_TABLE_NAME, disable=True)
 
53
 
 
54
 
31
55
def setup_module():
32
56
    global connection, table
33
 
    connection = happybase.Connection(host=HAPPYBASE_HOST,
34
 
                                      port=HAPPYBASE_PORT,
35
 
                                      table_prefix=TABLE_PREFIX,
36
 
                                      compat=HAPPYBASE_COMPAT)
 
57
    connection = Connection(**connection_kwargs)
 
58
 
37
59
    assert_is_not_none(connection)
38
60
 
 
61
    maybe_delete_table()
39
62
    cfs = {
40
63
        'cf1': {},
41
64
        'cf2': None,
42
65
        'cf3': {'max_versions': 1},
43
 
        }
 
66
    }
44
67
    connection.create_table(TEST_TABLE_NAME, families=cfs)
45
68
 
46
69
    table = connection.table(TEST_TABLE_NAME)
49
72
 
50
73
def teardown_module():
51
74
    if not KEEP_TABLE:
52
 
        connection.disable_table(TEST_TABLE_NAME)
53
 
        connection.delete_table(TEST_TABLE_NAME)
 
75
        connection.delete_table(TEST_TABLE_NAME, disable=True)
54
76
    connection.close()
55
77
 
56
78
 
57
79
def test_connection_compat():
58
80
    with assert_raises(ValueError):
59
 
        happybase.Connection(compat='0.1.invalid.version')
 
81
        Connection(compat='0.1.invalid.version')
 
82
 
 
83
 
 
84
def test_timeout_arg():
 
85
    Connection(
 
86
        timeout=5000,
 
87
        autoconnect=False)
60
88
 
61
89
 
62
90
def test_enabling():
79
107
    assert_equal(connection.table('foobar').name, TABLE_PREFIX + '_foobar')
80
108
    assert_equal(connection.table('foobar', use_prefix=False).name, 'foobar')
81
109
 
82
 
    c = happybase.Connection(autoconnect=False)
 
110
    c = Connection(autoconnect=False)
83
111
    assert_equal('foo', c._table_name('foo'))
84
112
 
85
113
    with assert_raises(TypeError):
86
 
        happybase.Connection(autoconnect=False, table_prefix=123)
 
114
        Connection(autoconnect=False, table_prefix=123)
87
115
 
88
116
    with assert_raises(TypeError):
89
 
        happybase.Connection(autoconnect=False, table_prefix_separator=2.1)
 
117
        Connection(autoconnect=False, table_prefix_separator=2.1)
90
118
 
91
119
 
92
120
def test_stringify():
128
156
def test_put():
129
157
    table.put('r1', {'cf1:c1': 'v1', 'cf1:c2': 'v2', 'cf2:c3': 'v3'})
130
158
    table.put('r1', {'cf1:c4': 'v2'}, timestamp=2345678)
 
159
    table.put('r1', {'cf1:c4': 'v2'}, timestamp=1369168852994L)
131
160
 
132
161
 
133
162
def test_atomic_counters():
159
188
 
160
189
    b = table.batch()
161
190
    b.put('row1', {'cf1:col1': 'value1',
162
 
                  'cf1:col2': 'value2'})
 
191
                   'cf1:col2': 'value2'})
163
192
    b.put('row2', {'cf1:col1': 'value1',
164
193
                   'cf1:col2': 'value2',
165
194
                   'cf1:col3': 'value3'})
391
420
    scanner = table.scan(row_prefix='row', timestamp=123)
392
421
    assert_equal(0, calc_len(scanner))
393
422
 
 
423
    scanner = table.scan(batch_size=20)
 
424
    next(scanner)
 
425
    next(scanner)
 
426
    scanner.close()
 
427
    with assert_raises(StopIteration):
 
428
        next(scanner)
 
429
 
394
430
 
395
431
def test_delete():
396
432
    row_key = 'row-test-delete'
417
453
 
418
454
    table.delete(row_key)
419
455
    assert_dict_equal({}, table.row(row_key))
 
456
 
 
457
 
 
458
def test_connection_pool_construction():
 
459
    with assert_raises(TypeError):
 
460
        ConnectionPool(size='abc')
 
461
 
 
462
    with assert_raises(ValueError):
 
463
        ConnectionPool(size=0)
 
464
 
 
465
 
 
466
def test_connection_pool():
 
467
 
 
468
    from thrift.transport.TTransport import TTransportException
 
469
 
 
470
    def run():
 
471
        name = threading.current_thread().name
 
472
        print "Thread %s starting" % name
 
473
 
 
474
        def inner_function():
 
475
            # Nested connection requests must return the same connection
 
476
            with pool.connection() as another_connection:
 
477
                assert connection is another_connection
 
478
 
 
479
                # Fake an exception once in a while
 
480
                if random.random() < .25:
 
481
                    print "Introducing random failure"
 
482
                    connection.transport.close()
 
483
                    raise TTransportException("Fake transport exception")
 
484
 
 
485
        for i in xrange(50):
 
486
            with pool.connection() as connection:
 
487
                connection.tables()
 
488
 
 
489
                try:
 
490
                    inner_function()
 
491
                except TTransportException:
 
492
                    # This error should have been picked up by the
 
493
                    # connection pool, and the connection should have
 
494
                    # been replaced by a fresh one
 
495
                    pass
 
496
 
 
497
                connection.tables()
 
498
 
 
499
        print "Thread %s done" % name
 
500
 
 
501
    N_THREADS = 10
 
502
 
 
503
    pool = ConnectionPool(size=3, **connection_kwargs)
 
504
    threads = [threading.Thread(target=run) for i in xrange(N_THREADS)]
 
505
 
 
506
    for t in threads:
 
507
        t.start()
 
508
 
 
509
    while threads:
 
510
        for t in threads:
 
511
            t.join(timeout=.1)
 
512
 
 
513
        # filter out finished threads
 
514
        threads = [t for t in threads if t.is_alive()]
 
515
        print "%d threads still alive" % len(threads)
 
516
 
 
517
 
 
518
def test_pool_exhaustion():
 
519
    pool = ConnectionPool(size=1, **connection_kwargs)
 
520
 
 
521
    def run():
 
522
        with assert_raises(NoConnectionsAvailable):
 
523
            with pool.connection(timeout=.1) as connection:
 
524
                connection.tables()
 
525
 
 
526
    with pool.connection():
 
527
        # At this point the only connection is assigned to this thread,
 
528
        # so another thread cannot obtain a connection at this point.
 
529
 
 
530
        t = threading.Thread(target=run)
 
531
        t.start()
 
532
        t.join()
 
533
 
 
534
 
 
535
if __name__ == '__main__':
 
536
    import logging
 
537
    import sys
 
538
 
 
539
    # Dump stacktraces using 'kill -USR1', useful for debugging hanging
 
540
    # programs and multi threading issues.
 
541
    try:
 
542
        import faulthandler
 
543
    except ImportError:
 
544
        pass
 
545
    else:
 
546
        import signal
 
547
        faulthandler.register(signal.SIGUSR1)
 
548
 
 
549
    logging.basicConfig(level=logging.DEBUG)
 
550
 
 
551
    method_name = 'test_%s' % sys.argv[1]
 
552
    method = globals()[method_name]
 
553
    method()