# Note: may be Apache 2.0 license-contaminated.
# (I haven't checked whether there is a significant license compatibility issue here.)

import os, logging
import tempfile
import windmill
from windmill.bin import admin_lib
from twisted.internet import defer
from foolscap.api import eventually
from allmydata.util import log, fileutil
from allmydata.scripts.create_node import create_node, create_introducer
from allmydata.scripts.startstop_node import do_start, do_stop
from time import sleep

class TiltingMixin:
  # adapted from
  # http://github.com/windmill/windmill/blob/master/windmill/authoring/unit.py
  # http://github.com/windmill/windmill/blob/master/windmill/bin/shell_objects.py
  
  def setUp(self):
    self.browser_debugging = True
    self.browser_name = "firefox"
    self.web_port = "9999"
    self.test_url = "/"
    self._js_test_details = []
    self.settings = {
      'EXIT_ON_DONE': True,
      'CONSOLE_LOG_LEVEL': logging.CRITICAL}
    
    fileutil.make_dirs("tilting")
    self.root = os.path.join(os.path.abspath("."), "tilting")
    self.public_html_path = os.path.join(self.root, "storage", "public_html")
    
    self.configure()
    
    log.msg("starting tahoe nodes")
    self._start_tahoe_nodes()
    
    log.msg("copying application")
    self.copy_application()
    
    log.msg("setting up Windmill for browser '%s'" % (self.browser_name))
    windmill.block_exit = True
    # Windmill loves to output all sorts of stuff
    windmill.stdout = tempfile.TemporaryFile()
    
    admin_lib.configure_global_settings(logging_on=False)
    for (setting, value) in self.settings.iteritems():
      windmill.settings[setting] = value
    
    windmill.settings['controllers'] = []
    windmill.settings['TEST_URL'] = 'http://127.0.0.1:%s/%s' % (self.web_port, self.test_url)
    
    self.shell_objects = admin_lib.setup()
    self.jsonrpc = self.shell_objects['httpd'].jsonrpc_methods_instance
    self.jsonrpc_app = self.shell_objects['httpd'].namespaces['windmill-jsonrpc']
    
    # Windmill prints success/failure statistics on its own
    # and this unfortunately seems to be the only way to stop it from doing that.
    # This is just a stripped down version of teardown method from windmill.server.convergence.JSONRPCMethods
    def _windmill_teardown(**kwargs):
      admin_lib.teardown(admin_lib.shell_objects_dict)
      windmill.runserver_running = False
      sleep(.25)
    
    #del self.jsonrpc_app.__dict__[u'teardown']
    self.jsonrpc_app.__dict__[u'teardown'] = _windmill_teardown
    
    if self.settings['JAVASCRIPT_TEST_DIR']:
      self._log_js_test_results()
    
    log.msg("starting browser")
    self.shell_objects['start_' + self.browser_name]()
    
    d = defer.Deferred()
    admin_lib.on_ide_awake.append(lambda: eventually(d.callback, None))
    d.addCallback(lambda ign: log.msg("browser started"))
    
    if self.browser_debugging:
      self.xmlrpc = windmill.tools.make_xmlrpc_client()
      d.addCallback(lambda ign:
        self.xmlrpc.add_command({'method':'commands.setOptions',
                                 'params':{'runTests':False, 'priority':'normal'}}))
    return d
  
  def tearDown(self):
    windmill.block_exit = False
    if self.browser_debugging:
      self.xmlrpc.add_command({'method':'commands.setOptions',
                               'params':{'runTests':True, 'priority':'normal'}})
    else:
      log.msg("shutting down browser '%s'" % (self.browser_name))
      admin_lib.teardown(self.shell_objects)
      log.msg("browser shutdown done")
    
    log.msg("shutting down Tahoe nodes")
    self._stop_tahoe_nodes()
    log.msg("Tahoe nodes shut down")
    fileutil.rm_dir("tilting")
  
  def _start_tahoe_nodes(self):
    out = tempfile.TemporaryFile()
    start_options = {'syslog': False, 'profile': None}
    
    create_introducer("tilting/introducer",
                      {'nickname': 'tilting-test-introducer'},
                      out=out, err=out)
    do_start("tilting/introducer", start_options, out=out, err=out)
    
    furl_path = "tilting/introducer/introducer.furl"
    # TODO: Once Python API for creating/starting/stopping/destroying nodes
    # is written this should be replaced
    while not os.path.isfile(furl_path):
      sleep(1)
    
    introducer_furl = fileutil.read(furl_path).strip()
    
    create_node("tilting/storage", {'introducer': introducer_furl,
                                    'nickname': 'tilting-test-storage-node',
                                    'webport': str(self.web_port)}, out=out, err=out)
    do_start("tilting/storage", start_options, out=out, err=out)
  
  def _stop_tahoe_nodes(self):
    out = tempfile.TemporaryFile()
    do_stop("tilting/introducer", out=out, err=out)
    do_stop("tilting/storage", out=out, err=out)
  
  def _log_js_test_results(self):
    # When running JS tests in windmill, only a "X tests of Y failed" string is printed
    # when all tests finished. This replaces Windmill's reporting method so that
    # all test results are collected in self._js_test_details and later reported
    # their succes is reported to Trial via self.failUnless(). This way Trial can easily
    # pickup which tests fail.
    
    def _report_without_resolve(**kwargs):
      self.jsonrpc._test_resolution_suite.report_without_resolve(*kwargs)
      self._js_test_details.append(kwargs)
      
      return 200
    
    del self.jsonrpc_app.__dict__[u'report_without_resolve']
    self.jsonrpc_app.register_method(_report_without_resolve, u'report_without_resolve')

class JSTestsMixin:
  """
  Mixin for running tests written in JavaScript.
  Remember to set self.settings['JS_TESTS_DIR'] (path is relative to _trial_tmp) as well as self.test_url.
  """
  
  def test_js(self):
    for test in self._js_test_details:
      self.failUnless(test['result'], test['debug'])

class Chrome(TiltingMixin, unittest.TestCase):
  """Starts tests in Chrome."""
  def configure(self):
    self.browser_name = "chrome"

class Firefox(TiltingMixin, unittest.TestCase):
  """Starts tests in Firefox."""
  def configure(self):
    self.browser_name = "firefox"

class InternetExplorer(TiltingMixin, unittest.TestCase):
  """Starts tests in Internet Explorer."""
  def configure(self):
    self.browser_name = "ie"

class Safari(TiltingMixin, unittest.TestCase):
  """Starts tests in Safari."""
  def configure(self):
    self.browser_name = "safari"