#!/usr/bin/env python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))))

import traceback
from remotexpcshelltests import RemoteXPCShellTestThread, XPCShellRemote, RemoteXPCShellOptions
from mozdevice import devicemanagerADB, DMError
from mozlog import structured
from mozlog.structured import commandline

DEVICE_TEST_ROOT = '/data/local/tests'


from marionette import Marionette

class B2GXPCShellTestThread(RemoteXPCShellTestThread):
    # Overridden
    def launchProcess(self, cmd, stdout, stderr, env, cwd, timeout=None):
        try:
            # This returns 1 even when tests pass - hardcode returncode to 0 (bug 773703)
            outputFile = RemoteXPCShellTestThread.launchProcess(self, cmd, stdout, stderr, env, cwd, timeout=timeout)
            self.shellReturnCode = 0
        except DMError:
            self.shellReturnCode = -1
            outputFile = "xpcshelloutput"
            f = open(outputFile, "a")
            f.write("\n%s" % traceback.format_exc())
            f.close()
        return outputFile

class B2GXPCShellRemote(XPCShellRemote):
    # Overridden
    def setLD_LIBRARY_PATH(self):
        self.env['LD_LIBRARY_PATH'] = '/system/b2g'
        if not self.options.use_device_libs:
            # overwrite /system/b2g if necessary
            XPCShellRemote.setLD_LIBRARY_PATH(self)

    # Overridden
    def setupUtilities(self):
        if self.options.clean:
            # Ensure a fresh directory structure for our tests
            self.clean()
            self.device.mkDir(self.options.remoteTestRoot)

        XPCShellRemote.setupUtilities(self)

    def clean(self):
        print >>sys.stderr, "\nCleaning files from previous run.."
        self.device.removeDir(self.options.remoteTestRoot)

    # Overriden
    def setupTestDir(self):
        if self.device._useZip:
            return XPCShellRemote.setupTestDir(self)

        for root, dirs, files in os.walk(self.xpcDir):
            for filename in files:
                rel_path = os.path.relpath(os.path.join(root, filename), self.xpcDir)
                test_file = os.path.join(self.remoteScriptsDir, rel_path)
                print 'pushing %s' % test_file
                self.device.pushFile(os.path.join(root, filename), test_file, retryLimit=10)

    # Overridden
    def pushLibs(self):
        if not self.options.use_device_libs:
            count = XPCShellRemote.pushLibs(self)
            if not count:
                # couldn't find any libs, fallback to device libs
                self.env['LD_LIBRARY_PATH'] = '/system/b2g'
                self.options.use_device_libs = True

class B2GOptions(RemoteXPCShellOptions):

    def __init__(self):
        RemoteXPCShellOptions.__init__(self)
        defaults = {}

        self.add_option('--b2gpath', action='store',
                        type='string', dest='b2g_path',
                        help="Path to B2G repo or qemu dir")
        defaults['b2g_path'] = None

        self.add_option('--emupath', action='store',
                        type='string', dest='emu_path',
                        help="Path to emulator folder (if different "
                                                      "from b2gpath")

        self.add_option('--no-clean', action='store_false',
                        dest='clean',
                        help="Do not clean TESTROOT. Saves [lots of] time")
        defaults['clean'] = True

        defaults['emu_path'] = None

        self.add_option('--emulator', action='store',
                        type='string', dest='emulator',
                        help="Architecture of emulator to use: x86 or arm")
        defaults['emulator'] = None

        self.add_option('--no-window', action='store_true',
                        dest='no_window',
                        help="Pass --no-window to the emulator")
        defaults['no_window'] = False

        self.add_option('--adbpath', action='store',
                        type='string', dest='adb_path',
                        help="Path to adb")
        defaults['adb_path'] = 'adb'

        self.add_option('--address', action='store',
                        type='string', dest='address',
                        help="host:port of running Gecko instance to connect to")
        defaults['address'] = None

        self.add_option('--use-device-libs', action='store_true',
                        dest='use_device_libs',
                        help="Don't push .so's")
        defaults['use_device_libs'] = False
        self.add_option("--gecko-path", action="store",
                        type="string", dest="geckoPath",
                        help="the path to a gecko distribution that should "
                        "be installed on the emulator prior to test")
        defaults["geckoPath"] = None
        self.add_option("--logdir", action="store",
                        type="string", dest="logdir",
                        help="directory to store log files")
        defaults["logdir"] = None
        self.add_option('--busybox', action='store',
                        type='string', dest='busybox',
                        help="Path to busybox binary to install on device")
        defaults['busybox'] = None

        defaults["remoteTestRoot"] = DEVICE_TEST_ROOT
        defaults['dm_trans'] = 'adb'
        defaults['debugger'] = None
        defaults['debuggerArgs'] = None

        self.set_defaults(**defaults)

    def verifyRemoteOptions(self, options):
        if options.b2g_path is None:
            self.error("Need to specify a --b2gpath")

        if options.geckoPath and not options.emulator:
            self.error("You must specify --emulator if you specify --gecko-path")

        if options.logdir and not options.emulator:
            self.error("You must specify --emulator if you specify --logdir")
        return RemoteXPCShellOptions.verifyRemoteOptions(self, options)

def run_remote_xpcshell(parser, options, args, log):
    options = parser.verifyRemoteOptions(options)

    # Create the Marionette instance
    kwargs = {}
    if options.emulator:
        kwargs['emulator'] = options.emulator
        if options.no_window:
            kwargs['noWindow'] = True
        if options.geckoPath:
            kwargs['gecko_path'] = options.geckoPath
        if options.logdir:
            kwargs['logdir'] = options.logdir
        if options.busybox:
            kwargs['busybox'] = options.busybox
        if options.symbolsPath:
            kwargs['symbols_path'] = options.symbolsPath
    if options.b2g_path:
        kwargs['homedir'] = options.emu_path or options.b2g_path
    if options.address:
        host, port = options.address.split(':')
        kwargs['host'] = host
        kwargs['port'] = int(port)
        kwargs['baseurl'] = 'http://%s:%d/' % (host, int(port))
        if options.emulator:
            kwargs['connectToRunningEmulator'] = True
    marionette = Marionette(**kwargs)

    if options.emulator:
        dm = marionette.emulator.dm
    else:
        # Create the DeviceManager instance
        kwargs = {'adbPath': options.adb_path}
        if options.deviceIP:
            kwargs['host'] = options.deviceIP
            kwargs['port'] = options.devicePort
        kwargs['deviceRoot'] = options.remoteTestRoot
        dm = devicemanagerADB.DeviceManagerADB(**kwargs)

    if not options.remoteTestRoot:
        options.remoteTestRoot = dm.deviceRoot
    xpcsh = B2GXPCShellRemote(dm, options, args, log)

    # we don't run concurrent tests on mobile
    options.sequential = True

    try:
        if not xpcsh.runTests(xpcshell='xpcshell', testdirs=args[0:],
                                 testClass=B2GXPCShellTestThread,
                                 mobileArgs=xpcsh.mobileArgs,
                                 **options.__dict__):
            sys.exit(1)
    except:
        print "Automation Error: Exception caught while running tests"
        traceback.print_exc()
        sys.exit(1)

def main():
    parser = B2GOptions()
    structured.commandline.add_logging_group(parser)
    options, args = parser.parse_args()
    log = commandline.setup_logging("Remote XPCShell",
                                    options,
                                    {"tbpl": sys.stdout})
    run_remote_xpcshell(parser, options, args, log)

# You usually run this like :
# python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--xre-path $MOZ_HOST_BIN
#                                                                               --adbpath $ADB_PATH
#                                                                               ...]
if __name__ == '__main__':
    main()
