""" Test some basic behaviour of msodde.py

Ensure that
- doc and docx are read without error
- garbage returns error return status
- dde-links are found where appropriate
"""

from __future__ import print_function

import unittest
from oletools import msodde
from tests.test_utils import DATA_BASE_DIR as BASE_DIR
import os
from os.path import join
from traceback import print_exc


class TestReturnCode(unittest.TestCase):
    """ check return codes and exception behaviour (not text output) """
    def test_valid_doc(self):
        """ check that a valid doc file leads to 0 exit status """
        for filename in (
                'dde-test-from-office2003', 'dde-test-from-office2016',
                'harmless-clean', 'dde-test-from-office2013-utf_16le-korean'):
            self.do_test_validity(join(BASE_DIR, 'msodde',
                                       filename + '.doc'))

    def test_valid_docx(self):
        """ check that a valid docx file leads to 0 exit status """
        for filename in 'dde-test', 'harmless-clean':
            self.do_test_validity(join(BASE_DIR, 'msodde',
                                       filename + '.docx'))

    def test_valid_docm(self):
        """ check that a valid docm file leads to 0 exit status """
        for filename in 'dde-test', 'harmless-clean':
            self.do_test_validity(join(BASE_DIR, 'msodde',
                                       filename + '.docm'))

    def test_valid_xml(self):
        """ check that xml leads to 0 exit status """
        for filename in 'harmless-clean-2003.xml', 'dde-in-excel2003.xml', \
                'dde-in-word2003.xml', 'dde-in-word2007.xml':
            self.do_test_validity(join(BASE_DIR, 'msodde', filename))

    def test_invalid_none(self):
        """ check that no file argument leads to non-zero exit status """
        self.do_test_validity('', True)

    def test_invalid_empty(self):
        """ check that empty file argument leads to non-zero exit status """
        self.do_test_validity(join(BASE_DIR, 'basic/empty'), True)

    def test_invalid_text(self):
        """ check that text file argument leads to non-zero exit status """
        self.do_test_validity(join(BASE_DIR, 'basic/text'), True)

    def test_encrypted(self):
        """
        check that encrypted files lead to non-zero exit status

        Currently, only the encryption applied by Office 2010 (CryptoApi RC4
        Encryption) is tested.
        """
        CRYPT_DIR = join(BASE_DIR, 'encrypted')
        ADD_ARGS = '', '-j', '-d', '-f', '-a'
        for filename in os.listdir(CRYPT_DIR):
            full_name = join(CRYPT_DIR, filename)
            for args in ADD_ARGS:
                self.do_test_validity(args + ' ' + full_name, True)

    def do_test_validity(self, args, expect_error=False):
        """ helper for test_valid_doc[x] """
        have_exception = False
        try:
            msodde.process_file(args, msodde.FIELD_FILTER_BLACKLIST)
        except Exception:
            have_exception = True
            print_exc()
        except SystemExit as exc:     # sys.exit() was called
            have_exception = True
            if exc.code is None:
                have_exception = False

        self.assertEqual(expect_error, have_exception,
                         msg='Args={0}, expect={1}, exc={2}'
                             .format(args, expect_error, have_exception))


class TestDdeLinks(unittest.TestCase):
    """ capture output of msodde and check dde-links are found correctly """

    @staticmethod
    def get_dde_from_output(output):
        """ helper to read dde links from captured output
        """
        return [o for o in output.splitlines()]

    def test_with_dde(self):
        """ check that dde links appear on stdout """
        filename = 'dde-test-from-office2003.doc'
        output = msodde.process_file(
            join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_BLACKLIST)
        self.assertNotEqual(len(self.get_dde_from_output(output)), 0,
                            msg='Found no dde links in output of ' + filename)

    def test_no_dde(self):
        """ check that no dde links appear on stdout """
        filename = 'harmless-clean.doc'
        output = msodde.process_file(
            join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_BLACKLIST)
        self.assertEqual(len(self.get_dde_from_output(output)), 0,
                         msg='Found dde links in output of ' + filename)

    def test_with_dde_utf16le(self):
        """ check that dde links appear on stdout """
        filename = 'dde-test-from-office2013-utf_16le-korean.doc'
        output = msodde.process_file(
            join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_BLACKLIST)
        self.assertNotEqual(len(self.get_dde_from_output(output)), 0,
                            msg='Found no dde links in output of ' + filename)

    def test_excel(self):
        """ check that dde links are found in excel 2007+ files """
        expect = ['cmd /c calc.exe', ]
        for extn in 'xlsx', 'xlsm', 'xlsb':
            output = msodde.process_file(
                join(BASE_DIR, 'msodde', 'dde-test.' + extn), msodde.FIELD_FILTER_BLACKLIST)

            self.assertEqual(expect, self.get_dde_from_output(output),
                             msg='unexpected output for dde-test.{0}: {1}'
                                 .format(extn, output))

    def test_xml(self):
        """ check that dde in xml from word / excel is found """
        for name_part in 'excel2003', 'word2003', 'word2007':
            filename = 'dde-in-' + name_part + '.xml'
            output = msodde.process_file(
                join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_BLACKLIST)
            links = self.get_dde_from_output(output)
            self.assertEqual(len(links), 1, 'found {0} dde-links in {1}'
                                            .format(len(links), filename))
            self.assertTrue('cmd' in links[0], 'no "cmd" in dde-link for {0}'
                                               .format(filename))
            self.assertTrue('calc' in links[0], 'no "calc" in dde-link for {0}'
                                                .format(filename))

    def test_clean_rtf_blacklist(self):
        """ find a lot of hyperlinks in rtf spec """
        filename = 'RTF-Spec-1.7.rtf'
        output = msodde.process_file(
            join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_BLACKLIST)
        self.assertEqual(len(self.get_dde_from_output(output)), 1413)

    def test_clean_rtf_ddeonly(self):
        """ find no dde links in rtf spec """
        filename = 'RTF-Spec-1.7.rtf'
        output = msodde.process_file(
            join(BASE_DIR, 'msodde', filename), msodde.FIELD_FILTER_DDE)
        self.assertEqual(len(self.get_dde_from_output(output)), 0,
                         msg='Found dde links in output of ' + filename)


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