import unittest
import tempfile
import os
from unittest.mock import patch, mock_open, MagicMock
import build
import buildreq
import config
import files
import tarball

class TestBuildpattern(unittest.TestCase):

    def test_simple_pattern_pkgconfig(self):
        """
        Test simple_pattern_pkgconfig with match
        """
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.simple_pattern_pkgconfig('line to test for testpkg.xyz',
                                     r'testpkg.xyz',
                                     'testpkg',
                                     False,
                                     reqs)
        self.assertIn('pkgconfig(testpkg)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_simple_pattern_pkgconfig_32bit(self):
        """
        Test simple_pattern_pkgconfig with match and 32bit option set
        """
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.simple_pattern_pkgconfig('line to test for testpkg.zyx',
                                     r'testpkg.zyx',
                                     'testpkgz',
                                     True,
                                     reqs)
        self.assertIn('pkgconfig(32testpkgz)', reqs.buildreqs)
        self.assertIn('pkgconfig(testpkgz)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_simple_pattern_pkgconfig_no_match(self):
        """
        Test simple_pattern_pkgconfig with no match, nothing should be modified
        """
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.simple_pattern_pkgconfig('line to test for somepkg.xyz',
                                     r'testpkg.xyz',
                                     'testpkg',
                                     False,
                                     reqs)
        self.assertEqual(reqs.buildreqs, set())
        self.assertEqual(pkg.must_restart, 0)

    def test_simple_pattern(self):
        """
        Test simple_pattern with match. The main difference between
        simple_pattern and simple_pattern_pkgconfig is the string that is added
        to buildreq.buildreqs.
        """
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.simple_pattern('line to test for testpkg.xyz',
                           r'testpkg.xyz',
                           'testpkg',
                           reqs)
        self.assertIn('testpkg', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_simple_pattern_no_match(self):
        """
        Test simple_pattern with no match, nothing should be modified
        """
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.simple_pattern('line to test for somepkg.xyz',
                           r'testpkg.xyz',
                           'testpkg',
                           reqs)
        self.assertEqual(reqs.buildreqs, set())
        self.assertEqual(pkg.must_restart, 0)

    def test_failed_pattern_no_match(self):
        """
        Test failed_pattern with no match
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: somepkg', conf, reqs, r'(test)', 0)
        self.assertEqual(reqs.buildreqs, set())
        self.assertEqual(pkg.must_restart, 0)

    def test_failed_pattern_no_buildtool(self):
        """
        Test failed_pattern with buildtool unset and initial match, but no
        match in failed_commands.
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: testpkg', conf, reqs, r'(test)', 0)
        self.assertEqual(reqs.buildreqs, set())
        self.assertEqual(pkg.must_restart, 0)

    def test_failed_pattern_no_buildtool_match(self):
        """
        Test failed_pattern with buildtool unset and match in failed_commands
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        conf.setup_patterns()
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: lex', conf, reqs, r'(lex)', 0)
        self.assertIn('flex', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_failed_pattern_pkgconfig(self):
        """
        Test failed_pattern with buildtool set to pkgconfig
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: testpkg.xyz',
                             conf,
                             reqs,
                             r'(testpkg)',
                             0,  # verbose=0
                             buildtool='pkgconfig')
        self.assertIn('pkgconfig(testpkg)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_failed_pattern_R(self):
        """
        Test failed_pattern with buildtool set to R
        """
        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: testpkg.r',
                             conf,
                             reqs,
                             r'(testpkg)',
                             0,  # verbose=0
                             buildtool='R')
        self.assertIn('R-testpkg', reqs.buildreqs)
        self.assertNotIn('R-testpkg', reqs.requires[None])
        self.assertEqual(pkg.must_restart, 1)

    def test_failed_pattern_perl(self):
        """
        Test failed_pattern with buildtool set to perl
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: testpkg.pl',
                             conf,
                             reqs,
                             r'(testpkg)',
                             0,  # verbose=0
                             buildtool='perl')
        self.assertIn('perl(testpkg)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_failed_pattern_pypi(self):
        """
        Test failed_pattern with buildtool set to pypi
        """
        conf = config.Config('')
        reqs = buildreq.Requirements("")
        pkg = build.Build()
        pkg.failed_pattern('line to test for failure: testpkg.py',
                           conf,
                           reqs,
                           r'(testpkg)',
                           0,  # verbose=0
                           buildtool='pypi')
        self.assertIn('pypi(testpkg)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_parse_buildroot_log_fail(self):
        """
        Test parse_buildroot_log with a test log indicating failure due to
        missing dependencies ('foobar' and 'foobarbaz')
        """
        def mock_util_call(cmd):
            del cmd

        call_backup = build.util.call
        build.util.call = mock_util_call

        open_name = 'build.util.open_auto'
        content = "line1\nDEBUG util.py:399:  No matching package to install: 'foobar'\nDEBUG util.py:399:  No matching package to install: 'foobarbaz'\nline 4"
        m_open = mock_open(read_data=content)
        pkg = build.Build()
        pkg.must_restart = 1
        pkg.file_restart = 1

        result = True
        with patch(open_name, m_open, create=True):
            result = pkg.parse_buildroot_log('testname', 1)

        build.util.call = call_backup

        self.assertFalse(result)
        self.assertEqual(pkg.must_restart, 0)
        self.assertEqual(pkg.file_restart, 0)

    def test_parse_buildroot_log_pass(self):
        """
        Test parse_buildroot_log with a test log indicating no failures
        """
        def mock_util_call(cmd):
            del cmd

        call_backup = build.util.call
        build.util.call = mock_util_call

        open_name = 'build.util.open_auto'
        content = "line 1\nline 2\nline 3\nline 4"
        m_open = mock_open(read_data=content)
        pkg = build.Build()

        result = True
        with patch(open_name, m_open, create=True):
            result = pkg.parse_buildroot_log('testname', 1)

        build.util.call = call_backup

        self.assertTrue(result)
        self.assertEqual(pkg.must_restart, 0)

    def test_parse_buildroot_log_noop(self):
        """
        Test parse_buildroot_log when parsing should be skipped (i.e. mock
        returned 0)
        """
        def mock_util_call(cmd):
            del cmd

        call_backup = build.util.call
        build.util.call = mock_util_call

        open_name = 'build.util.open_auto'
        content = "line 1\nline 2\nline 3\nline 4"
        m_open = mock_open(read_data=content)
        pkg = build.Build()

        result = True
        with patch(open_name, m_open, create=True):
            result = pkg.parse_buildroot_log('testname', 0)

        build.util.call = call_backup

        self.assertTrue(result)

    def test_parse_build_results_patch(self):
        """
        Test parse_build_results with a test log indicating failure due to a
        a backport patch no longer applying
        """
        def mock_util_call(cmd):
            del cmd

        def mock_conf_remove_backport_patch(patch):
            del patch
            return 1

        conf = config.Config('')
        conf.setup_patterns()
        conf.remove_backport_patch = mock_conf_remove_backport_patch
        conf.patches = ['backport-test.patch']
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        call_backup = build.util.call
        build.util.call = mock_util_call
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        open_name = 'build.util.open_auto'
        content = 'line 1\nPatch #1 (backport-test.patch):\nSkipping patch.'
        m_open = mock_open(read_data=content)

        with patch(open_name, m_open, create=True):
            pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

        build.util.call = call_backup

        self.assertEqual(pkg.must_restart, 1)

    def test_parse_build_results_pkgconfig(self):
        """
        Test parse_build_results with a test log indicating failure due to a
        missing qmake package (pkgconfig error)
        """
        def mock_util_call(cmd):
            del cmd

        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        conf.config_opts['32bit'] = True
        call_backup = build.util.call
        build.util.call = mock_util_call
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        open_name = 'build.util.open_auto'
        content = 'line 1\nwhich: no qmake\nexiting'
        m_open = mock_open(read_data=content)

        with patch(open_name, m_open, create=True):
            pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

        build.util.call = call_backup

        self.assertIn('pkgconfig(Qt)', reqs.buildreqs)
        self.assertIn('pkgconfig(32Qt)', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_parse_build_results_simple_pats(self):
        """
        Test parse_build_results with a test log indicating failure due to a
        missing httpd-dev package (simple pat error)
        """
        def mock_util_call(cmd):
            del cmd

        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        call_backup = build.util.call
        build.util.call = mock_util_call
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        open_name = 'build.util.open_auto'
        content = 'line 1\nchecking for Apache test module support\nexiting'
        m_open = mock_open(read_data=content)

        with patch(open_name, m_open, create=True):
            pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

        build.util.call = call_backup

        self.assertIn('httpd-dev', reqs.buildreqs)
        self.assertEqual(pkg.must_restart, 1)

    def test_parse_build_results_failed_pats(self):
        """
        Test parse_build_results with a test log indicating failure due to a
        missing package.
        """
        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        call_backup = build.util.call
        open_auto_backup = build.util.open_auto
        build.util.call = MagicMock(return_value=None)
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        with open('tests/builderrors', 'r') as f:
            builderrors = f.readlines()
            for error in builderrors:
                if not error.startswith('#'):
                    input, output = error.strip('\n').split('|')
                    reqs.buildreqs = set()
                    build.util.open_auto = mock_open(read_data=input)
                    pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

                    self.assertIn(output, reqs.buildreqs)
                    self.assertGreater(pkg.must_restart, 0)

        # Restoring functions
        build.util.call = call_backup
        build.util.open_auto = open_auto_backup

    def test_parse_build_results_files(self):
        """
        Test parse_build_results with a test log indicating files are missing
        """
        def mock_util_call(cmd):
            del cmd

        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        call_backup = build.util.call
        build.util.call = mock_util_call
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        open_name = 'build.util.open_auto'
        content = 'line 1\n' \
                  'Installed (but unpackaged) file(s) found:\n' \
                  '/usr/testdir/file\n' \
                  '/usr/testdir/file1\n' \
                  '/usr/testdir/file2\n' \
                  'RPM build errors\n' \
                  'errors here\n'
        m_open = mock_open(read_data=content)

        with patch(open_name, m_open, create=True):
            pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

        build.util.call = call_backup

        self.assertEqual(fm.files,
                         set(['/usr/testdir/file',
                              '/usr/testdir/file1',
                              '/usr/testdir/file2']))
        # one for each file added
        self.assertEqual(pkg.must_restart, 0)
        self.assertEqual(pkg.file_restart, 3)

    def test_parse_build_results_banned_files(self):
        """
        Test parse_build_results with a test log indicating banned files are missing
        """
        def mock_util_call(cmd):
            del cmd

        conf = config.Config('')
        conf.setup_patterns()
        reqs = buildreq.Requirements("")
        tcontent = tarball.Content("", "", "", [], conf, "/")
        call_backup = build.util.call
        build.util.call = mock_util_call
        pkg = build.Build()
        fm = files.FileManager(conf, pkg)

        open_name = 'build.util.open_auto'
        content = 'line 1\n' \
                  'Installed (but unpackaged) file(s) found:\n' \
                  '/opt/file\n' \
                  '/usr/etc/file\n' \
                  '/usr/local/file\n' \
                  '/usr/src/file\n' \
                  '/var/file\n' \
                  'RPM build errors\n' \
                  'errors here\n'
        m_open = mock_open(read_data=content)

        with patch(open_name, m_open, create=True):
            pkg.parse_build_results('testname', 0, fm, conf, reqs, tcontent)

        build.util.call = call_backup

        self.assertEqual(fm.has_banned, True)
        # check no files were added
        self.assertEqual(pkg.must_restart, 0)

    def test_get_mock_cmd_without_consolehelper(self):
        """
        Test get_mock_cmd when /usr/bin/mock doesn't point to consolehelper
        """
        def mock_realpath(path):
            return path

        realpath_backup = build.os.path.realpath

        build.os.path.realpath = mock_realpath

        mock_cmd = build.get_mock_cmd()

        build.os.path.realpath = realpath_backup

        self.assertEqual(mock_cmd, 'sudo /usr/bin/mock')

    def test_get_mock_cmd_with_consolehelper(self):
        """
        Test get_mock_cmd when /usr/bin/mock points to consolehelper
        """
        def mock_realpath(path):
            return '/usr/bin/consolehelper'

        realpath_backup = build.os.path.realpath

        build.os.path.realpath = mock_realpath

        mock_cmd = build.get_mock_cmd()

        build.os.path.realpath = realpath_backup

        self.assertEqual(mock_cmd, '/usr/bin/mock')


if __name__ == '__main__':
    unittest.main(buffer=True)
