# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
# Copyright 2012-2014 Canonical, Ltd.
# Author: Thomi Richards
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.

"""Functional tests for the window-mocker application."""

import json
import os
import subprocess
import tempfile

from time import sleep

from testtools import TestCase
from testtools.matchers import Contains, Equals, MatchesAny


def get_app_path():
    """Return the full path to the test application.

    This function prefers the local copy if these tests are being run from
    source.

    """
    source_path = os.path.abspath(
        os.path.join(
            os.path.dirname(__file__),
            '..',
            'bin',
            'window-mocker'))
    if os.path.exists(source_path):
        return source_path

    return subprocess.check_output(['which', 'window-mocker'],
                                   universal_newlines=True)


def run_app_with_args(*args):
    os.putenv('PYTHONPATH',
        os.path.abspath(
            os.path.join(
                os.path.dirname(__file__),
                '..')
            )
        )
    application = [get_app_path()]
    application.extend(args)
    process = subprocess.Popen(
            application,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            shell=False,
            universal_newlines=True,
            )

    stdout = stderr = ""
    for i in range(3):
        retcode = process.poll()
        if retcode is not None:
            break
        if i == 1:
            print("Terminating process.")
            process.terminate()
        if i == 2:
            print("Killing process.")
            process.kill()
        sleep(1)
    stdout, stderr = process.communicate()
    return (retcode, stdout, stderr)


class FunctionalTests(TestCase):

    """Functional tests for the window-mocker application."""

    def run_with_spec_content(self, content_string):
        fd, temp_filename = tempfile.mkstemp()
        self.addCleanup(os.remove, temp_filename)
        os.close(fd)
        with open(temp_filename, 'w') as f:
            f.write(content_string)
        return run_app_with_args(temp_filename)

    def test_spec_with_menus(self):
        window_spec = {
            "Title": "Test menu application",
            "Menu": ["Search entry", "Quit"],
        }
        fd, temp_filename = tempfile.mkstemp()
        self.addCleanup(os.remove, temp_filename)
        os.close(fd)
        with open(temp_filename, 'w') as f:
            f.write(json.dumps(window_spec))
        return run_app_with_args(temp_filename)

    def test_errors_with_missing_file(self):
        retcode, stdout, stderr = run_app_with_args("bad_file_name")
        self.assertThat(stdout, Equals(''))
        self.assertThat(stderr, Equals('Error: Unable to open file: No such file or directory\n'))
        self.assertThat(retcode, Equals(1))

    def test_errors_with_inaccessible_file(self):
        fd, temp_filename = tempfile.mkstemp()
        self.addCleanup(os.remove, temp_filename)
        os.close(fd)
        with open(temp_filename, 'w'):
            pass
        os.chmod(temp_filename, 0)

        retcode, stdout, stderr = run_app_with_args(temp_filename)
        self.assertThat(stdout, Equals(''))
        self.assertThat(stderr, Equals('Error: Unable to open file: Permission denied\n'))
        self.assertThat(retcode, Equals(1))

    def test_errors_on_empty_file(self):
        retcode, stdout, stderr = self.run_with_spec_content("")

        self.assertThat(stdout, Equals(''))
        self.assertThat(retcode, Equals(3))
        self.assertThat(
            stderr,
            MatchesAny(
                Equals('Error: Unable to process request: No JSON object could be decoded\n'),
                Equals('Error: Unable to process request: Expecting value: line 1 column 1 (char 0)\n'),
            )
        )

    def test_accepts_testability_command_line_argument(self):
        retcode, stdout, stderr = run_app_with_args("--help")
        self.assertThat(stdout, Contains("-testability"))

    def test_accepts_plugin_type_command_line_argument(self):
        retcode, stdout, stderr = run_app_with_args("--help")
        self.assertThat(stdout, Contains("Qt4"))
