#!/usr/bin/env python
#
# Public Domain 2014-2016 MongoDB, Inc.
# Public Domain 2008-2014 WiredTiger, Inc.
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

import helper, wiredtiger, wttest
from wiredtiger import stat
from helper import key_populate, simple_populate
from wtscenario import make_scenarios

# test_stat01.py
#    Statistics operations
class test_stat01(wttest.WiredTigerTestCase):
    """
    Test statistics
    """

    config = 'internal_page_max=4K,leaf_page_max=8K'
    nentries = 25

    types = [
        ('file', dict(uri='file:test_stat01.wt')),
        ('table', dict(uri='table:test_stat01.wt'))
    ]
    keyfmt = [
        ('recno', dict(keyfmt='r')),
        ('string', dict(keyfmt='S')),
    ]
    scenarios = make_scenarios(types, keyfmt)

    conn_config = 'statistics=(all)'

    def statstr_to_int(self, str):
        """
        Convert a statistics value string, which may be in either form:
        '12345' or '33M (33604836)'
        """
        parts = str.rpartition('(')
        return int(parts[2].rstrip(')'))

    # Do a quick check of the entries in the the stats cursor, the "lookfor"
    # string should appear with a minimum value of least "min".
    def check_stats(self, statcursor, min, lookfor):
        stringclass = ''.__class__
        intclass = (0).__class__

        # Reset the cursor, we're called multiple times.
        statcursor.reset()

        found = False
        foundval = 0
        for id, desc, valstr, val in statcursor:
            self.assertEqual(type(desc), stringclass)
            self.assertEqual(type(valstr), stringclass)
            self.assertEqual(type(val), intclass)
            self.assertEqual(val, self.statstr_to_int(valstr))
            self.printVerbose(2, '  stat: \'' + desc + '\', \'' +
                              valstr + '\', ' + str(val))
            if desc == lookfor:
                found = True
                foundval = val

        self.assertTrue(found, 'in stats, did not see: ' + lookfor)
        self.assertTrue(foundval >= min)

    # Test simple connection statistics.
    def test_basic_conn_stats(self):
        # Build an object and force some writes.
        config = self.config + ',key_format=' + self.keyfmt
        simple_populate(self, self.uri, config, 1000)
        self.session.checkpoint(None)

        # See that we can get a specific stat value by its key and verify its
        # entry is self-consistent.
        allstat_cursor = self.session.open_cursor('statistics:', None, None)
        self.check_stats(allstat_cursor, 10, 'block-manager: blocks written')

        values = allstat_cursor[stat.conn.block_write]
        self.assertEqual(values[0], 'block-manager: blocks written')
        val = self.statstr_to_int(values[1])
        self.assertEqual(val, values[2])
        allstat_cursor.close()

    # Test simple object statistics.
    def test_basic_data_source_stats(self):
        # Build an object.
        config = self.config + ',key_format=' + self.keyfmt
        self.session.create(self.uri, config)
        cursor = self.session.open_cursor(self.uri, None, None)
        value = ""
        for i in range(1, self.nentries):
            value = value + 1000 * "a"
            cursor[key_populate(cursor, i)] = value
        cursor.close()

        # Force the object to disk, otherwise we can't check the overflow count.
        self.reopen_conn()

        # See that we can get a specific stat value by its key and verify its
        # entry is self-consistent.
        cursor = self.session.open_cursor('statistics:' + self.uri, None, None)
        self.check_stats(cursor, 8192, 'btree: maximum leaf page size')
        self.check_stats(cursor, 4096, 'btree: maximum internal page size')
        self.check_stats(cursor, 10, 'btree: overflow pages')

        values = cursor[stat.dsrc.btree_overflow]
        self.assertEqual(values[0], 'btree: overflow pages')
        val = self.statstr_to_int(values[1])
        self.assertEqual(val, values[2])
        cursor.close()

        cursor = self.session.open_cursor(
            'statistics:' + self.uri, None, "statistics=(size)")
        values = cursor[stat.dsrc.block_size]
        self.assertNotEqual(values[2], 0)
        cursor.close()

    # Test simple per-checkpoint statistics.
    def test_checkpoint_stats(self):
        for name in ('first', 'second', 'third'):
            config = self.config + ',key_format=' + self.keyfmt
            helper.simple_populate(self, self.uri, config, self.nentries)
            self.session.checkpoint('name=' + name)
            cursor = self.session.open_cursor(
                'statistics:' + self.uri, None, 'checkpoint=' + name)
            self.assertEqual(
                cursor[stat.dsrc.btree_entries][2], self.nentries)
            cursor.close()

    def test_missing_file_stats(self):
        self.assertRaises(wiredtiger.WiredTigerError, lambda:
            self.session.open_cursor('statistics:file:DoesNotExist'))

if __name__ == '__main__':
    wttest.run()
