#!/usr/bin/env python
#
# Public Domain 2014-present 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.
#
# test_compat03.py
# Check compatibility API

import fnmatch, os
import wiredtiger, wttest
from suite_subprocess import suite_subprocess
from wtdataset import SimpleDataSet, simple_key
from wtscenario import make_scenarios

class test_compat03(wttest.WiredTigerTestCase, suite_subprocess):
    # Add enough entries and use a small log size to generate more than
    # one log file.
    entries = 2000
    logmax = "100K"
    tablename = 'test_compat03'
    uri = 'table:' + tablename
    # Declare the log versions that do and do not have prevlsn.
    # Log version 1 does not have the prevlsn record.
    # Log version 2 introduced that record.
    # Log versions 3 and higher continue to have that record.
    min_logv = 2

    # Test detecting a not-yet-existing log version. This should
    # hold us for many years.
    future_logv = 20
    future_rel = "20.0"

    # The API uses only the major and minor numbers but accepts with
    # and without the patch number. Test one on release and the
    # required minimum just for testing of parsing.
    compat_release = [
        ('future_rel', dict(rel=future_rel, log_rel=future_logv)),
        ('def_rel', dict(rel='none', log_rel=5)),
        ('100_rel', dict(rel="10.0", log_rel=5)),
        ('33_rel', dict(rel="3.3", log_rel=4)),
        ('32_rel', dict(rel="3.2", log_rel=3)),
        ('31_rel', dict(rel="3.1", log_rel=3)),
        ('30_rel', dict(rel="3.0", log_rel=2)),
        ('26_rel', dict(rel="2.6", log_rel=1)),
        ('26_patch_rel', dict(rel="2.6.1", log_rel=1)),
    ]

    # Only the maximum version should exist below for each log version
    # i.e. even though 3.1 is also log_max=3 3.2 is above it and also
    # log_max=3 as such we don't need 3.1 in this list.
    # However the exemption to this rule is versions which include a patch
    # number as the patch number will get removed in the conn_reconfig.c
    # This rule exemption applies to the minimum verison check as well.
    compat_max = [
        ('future_max', dict(max_req=future_rel, log_max=future_logv)),
        ('def_max', dict(max_req='none', log_max=5)),
        ('100_max', dict(max_req="10.0", log_max=5)),
        ('33_max', dict(max_req="3.3", log_max=4)),
        ('32_max', dict(max_req="3.2", log_max=3)),
        ('30_max', dict(max_req="3.0", log_max=2)),
        ('26_max', dict(max_req="2.6", log_max=1)),
        ('26_patch_max', dict(max_req="2.6.1", log_max=1)),
    ]

    # Only the minimum version should exist below for each log version.
    compat_min = [
        ('future_min', dict(min_req=future_rel, log_min=future_logv)),
        ('def_min', dict(min_req='none', log_min=5)),
        ('100_min', dict(min_req="10.0", log_min=5)),
        ('33_min', dict(min_req="3.3", log_min=4)),
        ('31_min', dict(min_req="3.1", log_min=3)),
        ('30_min', dict(min_req="3.0", log_min=2)),
        ('26_min', dict(min_req="2.6", log_min=1)),
        ('26_patch_min', dict(min_req="2.6.1", log_min=1)),
    ]

    scenarios = make_scenarios(compat_release, compat_max, compat_min)

    # This test creates scenarios that lead to errors. This is different
    # than compat02 because it is testing errors (or success) using the
    # compatibility settings on the initial database creation.
    def test_compat03(self):
        testdir = 'TEST'
        os.mkdir(testdir)
        config_str = 'create,'
        log_str = 'log=(archive=false,enabled,file_max=%s),' % self.logmax
        compat_str = ''

        if (self.rel != 'none'):
            compat_str += 'compatibility=(release="%s"),' % self.rel
        if (self.max_req != 'none'):
            compat_str += 'compatibility=(require_max="%s"),' % self.max_req
        if (self.min_req != 'none'):
            compat_str += 'compatibility=(require_min="%s"),' % self.min_req
        config_str += log_str + compat_str
        self.pr("Conn config:" + config_str)

        # We have a lot of error cases. There are too many and they are
        # dependent on the order of the library code so don't check specific
        # error messages. So just determine if an error should occur and
        # make sure it does.

        if ((self.log_min >= self.future_logv) or
          (self.log_max >= self.future_logv) or
          (self.log_rel >= self.future_logv) or
          (self.max_req != 'none' and self.log_max < self.log_rel) or
          (self.min_req != 'none' and self.log_min > self.log_rel) or
          (self.max_req != 'none' and self.min_req != 'none' and self.log_max < self.log_min)):
            expect_err = True
        else:
            expect_err = False

        if (expect_err == True):
            msg = '/Version incompatibility detected:/'
            self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
                lambda: self.wiredtiger_open(testdir, config_str), msg)
        else:
            self.pr("EXPECT SUCCESS")
            conn = self.wiredtiger_open(testdir, config_str)
            conn.close()

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