Skip to content
Snippets Groups Projects
sth.js 27.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • NikSurya's avatar
    NikSurya committed
    /// Copyright (c) 2012 Ecma International.  All rights reserved.
    
    /// Ecma International makes this code available under the terms and conditions set
    
    NikSurya's avatar
    NikSurya committed
    /// forth on http://hg.ecmascript.org/tests/test262/raw-file/tip/LICENSE (the
    /// "Use Terms").   Any redistribution of this code must retain the above
    
    /// copyright and this notice and otherwise comply with the Use Terms.
    
    // Do not cache any JSON files - see
    // https://bugs.ecmascript.org/show_bug.cgi?id=87
    
    /*
     * Run a test in the browser. Works by injecting an iframe with the test code.
     *
     * Public Methods:
     * * run(id, test): Runs the test specified.
     *
     * Callbacks:
    
     * * onComplete(test): Called when the test is run. Test object
     *                     contains result and error strings describing how the
    
     *                     test ran.
     */
    function BrowserRunner() {
        var iframe,             // injected iframe
            currentTest,        // Current test being run.
    
    David Fugate's avatar
    David Fugate committed
            scriptCache = {},   // Holds the various includes required to run certain tests.
            instance    = this,
            errorDetectorFileContents,
            simpleTestAPIContents,
            globalScopeContents,
    
            assertContents,
    
    NikSurya's avatar
    NikSurya committed
            timerContents,
            startTime,
    
            harnessDir = "harness/";
    
    David Fugate's avatar
    David Fugate committed
    
    
        $.ajax({async: false,
                dataType: "text",
                success: function(data){errorDetectorFileContents = data;},
    
    David Fugate's avatar
    David Fugate committed
                url:harnessDir+"ed.js"});
    
    
        $.ajax({async: false,
                dataType: "text",
                success: function(data){simpleTestAPIContents = data;},
    
    David Fugate's avatar
    David Fugate committed
                url:harnessDir+"sta.js"});
    
    
        $.ajax({async: false,
                dataType: "text",
                success: function(data){globalScopeContents = data;},
    
    David Fugate's avatar
    David Fugate committed
                url:harnessDir+"gs.js"});
    
    
        $.ajax({async: false,
                dataType: "text",
                success: function(data){assertContents = data;},
                url:harnessDir+"assert.js"});
    
    NikSurya's avatar
    NikSurya committed
        
    	$.ajax({async: false,
    		dataType: "text",
    		success: function(data){timerContents = data;},
    		url:harnessDir+"timer.js"});
    
        /* Called by the child window to notify that the test has
         * finished. This function call is put in a separate script block
         * at the end of the page so errors in the test script block
         * should not prevent this function from being called.
    
    David Fugate's avatar
    David Fugate committed
            if((typeof currentTest.result) === "undefined") {
    
                // We didn't get a call to testRun, which likely means the
                // test failed to load.
    
    David Fugate's avatar
    David Fugate committed
                currentTest.error  = "Failed to load test case (probable parse error).";
                currentTest.description = "Failed to load test case!";
            } else if((typeof currentTest.error) !== "undefined") {
    
                if(currentTest.error instanceof Test262Error) {
    
                    currentTest.error = currentTest.message;
                } else {
    
    David Fugate's avatar
    David Fugate committed
                currentTest.error = currentTest.error.name + ": " + currentTest.error.message;
    
    David Fugate's avatar
    David Fugate committed
            } else if ((typeof currentTest.error === "undefined") && (currentTest.result === "fail")) {
                currentTest.error = "Test case returned non-true value.";
    
    NikSurya's avatar
    NikSurya committed
            //update elapsed time
            controller.testElapsedTime(new Date() - startTime);
    
        /* Called from the child window after the test has run. */
    
        function testRun(id, path, description, codeString, result, error) {
    
            currentTest.id = id;
            currentTest.path = path;
            currentTest.description = description;
            currentTest.result = result;
            currentTest.error = error;
            currentTest.code = codeString;
    
        function isAsyncTest(code) {
    
            return /\$DONE()/.test(code);
    
    David Fugate's avatar
    David Fugate committed
        this.run = function (test, code) {
    
    NikSurya's avatar
    NikSurya committed
            startTime = new Date();
    
    
            //--Detect proper window.onerror support
            if (instance.supportsWindowOnerror===undefined) {
                var iframePrereqs = document.createElement("iframe");
                iframePrereqs.setAttribute("id", "prereqsIframe");
                if (!/firefox/i.test(navigator.userAgent)) {
                    iframePrereqs.setAttribute("style", "display:none");
                }
                document.body.appendChild(iframePrereqs);
    
    
    NikSurya's avatar
    NikSurya committed
                var iwinPrereqs = iframePrereqs.contentWindow;
    
                var idocPrereqs = iwinPrereqs.document;
                idocPrereqs.open();
    
                iwinPrereqs.failCount = 0;
    
                var stuff = [
                             "window.onerror = function(a, b, c) { this.failCount++; }",
                             "va xyz =",
                             "throw Error();"
                ];
    
                for(var i in stuff) {
                    idocPrereqs.writeln("<script type='text/javascript'>");
                    idocPrereqs.writeln(stuff[i]);
                    idocPrereqs.writeln("</script>");
                }
                idocPrereqs.close();
    
                //TODO - 500ms *should* be a sufficient delay
                setTimeout(function() {
    
                    instance.supportsWindowOnerror = (iwinPrereqs.failCount === 2);
    
                    //alert(iwinPrereqs.failCount);
                    document.body.removeChild(iframePrereqs);
                    instance.run(test, code);
                }, 500);
    
                return 0; // initial config, ignore this timing.
    
            currentTest = {};
            for (var tempIndex in test) {
                if (test.hasOwnProperty(tempIndex)) {
                    currentTest[tempIndex] = test[tempIndex];
                }
            }
            currentTest.code = code;
    
    
            iframe.setAttribute("id", "runnerIframe");
    
    David Fugate's avatar
    David Fugate committed
            //FireFox has a defect where it doesn't fire window.onerror for an iframe if the iframe
            //is invisible.
    
            if (!/firefox/i.test(navigator.userAgent)) {
    
    David Fugate's avatar
    David Fugate committed
                iframe.setAttribute("style", "display:none");
            }
    
    David Fugate's avatar
    David Fugate committed
            var iwin = window.frames[window.frames.length - 1];
            var idoc = iwin.document;
            idoc.open();
    
    David Fugate's avatar
    David Fugate committed
            iwin.testRun = testRun;
            iwin.testFinished = testFinished;
    
            //TODO: these should be moved to sta.js
    
            var includes,
    
    David Fugate's avatar
    David Fugate committed
                include;
    
            
            includes = test.includes;
            if (!includes || !(includes.length)) {
                // includes not specified via frontmatter;  find all of the $INCLUDE statements
                includes = code.match(/\$INCLUDE\(([^\)]+)\)/g);
            }
    
            if (includes !== null) {
    
                // We have some includes, so loop through each include and
                // pull in the dependencies.
    
                for (var i = 0; i < includes.length; i++) {
    
                    include = includes[i].replace(/.*\(('|")(.*)('|")\)/, "$2");
    
    
                    // First check to see if we have this script cached
                    // already, and if not, grab it.
    
                    if (typeof scriptCache[include] === "undefined") {
    
                            url: 'harness/' + include,
                            success: function (s) { scriptCache[include] = s; }
    
                    // Finally, write the required script to the window.
    
    David Fugate's avatar
    David Fugate committed
                    idoc.writeln("<script type='text/javascript'>" + scriptCache[include] + "</script>");
    
            //Write out all of our helper functions
    
    David Fugate's avatar
    David Fugate committed
            //idoc.writeln("<script type='text/javascript' src='harness/sta.js'>" + "</script>");
            idoc.writeln("<script type='text/javascript'>");
            idoc.writeln(simpleTestAPIContents);
            idoc.writeln("</script>");
    
            iwin.iframeError = undefined;
            iwin.onerror = undefined;
            iwin.testDescrip = currentTest;
    
            //Add an error handler capable of catching so-called early errors
    
    NikSurya's avatar
    NikSurya committed
    		//idoc.writeln("<script type='text/javascript' src='harness/ed.js'>" + "</script>")
    
            idoc.writeln("<script type='text/javascript'>");
            idoc.writeln(errorDetectorFileContents);
            idoc.writeln("</script>");
    
    
    NikSurya's avatar
    NikSurya committed
            //Validate the results
            //idoc.writeln("<script type='text/javascript' src='harness/gs.js' defer>" + "</script>");
            idoc.writeln("<script type='text/javascript'>");
            idoc.writeln(globalScopeContents);
            idoc.writeln("</script>");
    
    
            idoc.writeln("<script type='text/javascript'>");
            idoc.writeln(assertContents);
            idoc.writeln("</script>");
    
    
    NikSurya's avatar
    NikSurya committed
            //this is mainly applicable for consoles that do not have setTimeout support
    		//idoc.writeln("<script type='text/javascript' src='harness/timer.js' defer>" + "</script>");
    
            if(setTimeout === undefined && isAsyncTest(code)) {
            idoc.writeln("<script type='text/javascript'>");
    
    NikSurya's avatar
    NikSurya committed
             idoc.writeln(timerContents);
             idoc.writeln("</script>");
            }
    
    
            //Run the code
            idoc.writeln("<script type='text/javascript'>");
    
            idoc.writeln(this.compileSource(test, code));
    
            idoc.writeln("</script>");
    
            idoc.writeln("<script type='text/javascript'>");
    
            if (!isAsyncTest(code)) {
                //if the test is synchronous - call $DONE immediately
    
    NikSurya's avatar
    NikSurya committed
                idoc.writeln("if(typeof $DONE === 'function') $DONE()");
    
            } else {
                //in case the test does not call $DONE asynchronously then
                //bailout after 1 min or given bailout time by calling $DONE
    
    NikSurya's avatar
    NikSurya committed
                var asyncval = parseInt(test.timeout);
                var testTimeout = asyncval !== asyncval ? 2000 : asyncval;
    
    	    idoc.writeln("setTimeout(function() {$ERROR(\" Test Timed Out at " + testTimeout +"\" )} ," + testTimeout + ")");
    
            idoc.writeln("</script>");
    
    David Fugate's avatar
    David Fugate committed
            idoc.close();
    
    David Fugate's avatar
    David Fugate committed
        //--Helper functions-------------------------------------------------------
        this.convertForEval = function(txt) {
            txt = txt.replace(/\\/g,"\\\\");
            txt = txt.replace(/\"/g,"\\\"");
            txt = txt.replace(/\'/g,"\\\'");
            txt = txt.replace(/\r/g,"\\r");
            txt = txt.replace(/\n/g,"\\n");
            return txt;
    
    /**
     * Transform the test source code according to the test metadata and the
     * capabilities of the current environment.
     *
     * @param {object} test - a test object as retrieved by TestLoader
     * @param {string} code - unmodified test source code
     *
     * @returns {string} the transformed source code
     */
    BrowserRunner.prototype.compileSource = function(test, code) {
        var flags = test.flags;
    
        if (flags && flags.indexOf("onlyStrict") > -1) {
            code = "'use strict';\n" + code;
        }
    
        if (!this.supportsWindowOnerror) {
            code = "try {eval(\"" + this.convertForEval(code) +
                "\");} catch(e) {window.onerror(e.toString(), null, null);}";
        }
    
        return code;
    };
    
    
    David Fugate's avatar
    David Fugate committed
    /* Loads tests from the sections specified in testcases.json.
    
     * Public Methods:
     * * getNextTest() - Start loading the next test.
     * * reset() - Start over at the first test.
     *
     * Callbacks:
    
    David Fugate's avatar
    David Fugate committed
     * * onLoadingNextSection(path): Called after a request is sent for the next section json, with the path to that json.
    
     * * onInitialized(totalTests): Called after the testcases.json is loaded and parsed.
    
     * * onTestReady(id, code): Called when a test is ready with the
     *       test's id and code.
    
     * * onTestsExhausted(): Called when there are no more tests to run.
     */
    function TestLoader() {
    
        var testGroups       = [],
    
            loader           = this,
            mode             = "all";
    
        this.loadedFiles = 0;
        this.version     = undefined;
        this.date        = undefined;
        this.totalTests  = 0;
        this.runningTests = 0;
    
    
        /* Get the XML for the next section */
        function getNextXML() {
            currentTestIndex = 0;
    
    
            // already loaded this section.
            if(testGroups[testGroupIndex].status == 'loaded') {
                testGroups[testGroupIndex].onLoaded = function(){};
    
            // not loaded, so we attach a callback to come back here when the file loads.
            else {
                presenter.updateStatus("Loading file: " + testGroups[testGroupIndex].path);
                testGroups[testGroupIndex].onLoaded = getNextXML;
    
        /* Get the test list xml */
        function loadTestXML() {
            var testsListLoader = new XMLHttpRequest();
    
            $.ajax({url: TEST_LIST_PATH, dataType: 'json', success: function(data) {
                var testSuite = data.testSuite;
    
                loader.version    = data.version;
                loader.date       = data.date;
    
                loader.totalTests = data.numTests;
    
                for (var i = 0; i < testSuite.length; i++) {
    
                        tests: [],
                        selected: false,
                        status: 'waiting',
                        onLoaded: function(){}
    
                    presenter.setTestWaiting(i, testSuite[i]);
    
                    var tr = $('#chapterSelector table tr').filter(':nth-child(' + (i+1) + ')');
                    tr.find('img').filter('[alt="Run"]').bind('click', {index:i}, function(event){
                        controller.runIndividualTest(event.data.index);
                    });
    
                loader.onInitialized(loader.totalTests);
    
        /* Get the test file. Handles all the logic of figuring out the next file to load. */
        function getFile(index) {
            index = (arguments.length == 0) ? -1 : index;
    
            // Look for selected waiting chapters (priority because you'll probably want to run them soon)
            for(var i = 0; index == -1 && i < testGroups.length; i++) {
                if(testGroups[i].status == 'waiting' && testGroups[i].selected) {
                    index = i;
                }
            }
    
            // Look for just chapters waiting to be loaded.
            for(var i = 0; index == -1 && i < testGroups.length; i++) {
                if(testGroups[i].status == 'waiting') {
                    index = i;
                }
            }
    
            if(index == -1) {
                // Still -1? No more tests are waiting to be loaded then.
                if(controller.state == 'loading') {
                    presenter.setState('loaded');
                }
                return;
            }
    
            presenter.setTestLoading(index, testGroups[index].path);
            // the only other status that should be set when we get here is 'priorityloading'
            if(testGroups[index].status == 'waiting') {
                testGroups[index].status = 'loading';
            }
    
            loader.onTestStartLoading(index, testGroups[index].path);
            // Create the AJAX call to grab the file.
            $.ajax({
                url: testGroups[index].path,
                dataType: 'json',
                // Callback with the chapter name and number of tests.
                success: function(data, status, xhr) {
                    // Save the data for later usage
                    testGroups[index].tests = data.testsCollection.tests;
                    onTestLoaded(index, data.testsCollection.name, data.testsCollection.tests.length);
                },
                error: function(xhr, textStatus, errorThrown) {
                    // TODO: Catch this error and update UI accordingly. Unlikely to happen, but errors would be 404 or 5-- errors.
    
        /* Executes when a test file finishes loading. */
        function onTestLoaded(index, name, numTests) {
            presenter.setTestLoaded(index, name, numTests);
    
            if(testGroups[index].selected && mode == "multiple") {
                loader.runningTests += numTests;
                loader.onInitialized( loader.runningTests );
            }
    
            // The loading status is only assigned when downloading files in sequence, otherwise it
            // gets the status of priorityloading. When loading out of order, we only want to download
            // the single file, so we'll only tell it to get the next file when we see a status of
            // loading.
            if(testGroups[index].status == 'loading') {
                getFile(); // triggers downloading the next file
                testGroups[index].status = 'loaded';
            }
            else if(testGroups[index].status == 'priorityloading') {
                // Run the test
                testGroups[index].status = 'loaded';
                loader.setChapter(index);
            }
    
            testGroups[index].onLoaded();
        };
    
        function getIdFromPath (path) {
    
            //path is of the form "a/b/c.js"
    
            var id = path.split("/");
            //id is now of the form ["a", "b", "c.js"];
    
            id = id[id.length-1];
            //id is now of the form "c.js"
    
            id = id.replace(/\.js$/i, "");
            //id is now of the form "c"
    
            return id;
        }
    
    
        /* Move on to the next test */
        this.getNextTest = function() {
    
            // If the file is loaded
            if(testGroups[testGroupIndex].status == "loaded")
            {
                // And if we have tests left in this file
                if(currentTestIndex < testGroups[testGroupIndex].tests.length) {
                    // Run the next test
                    var test = testGroups[testGroupIndex].tests[currentTestIndex++];
                    var scriptCode = test.code;
                    test.id = getIdFromPath(test.path);
    
                    loader.onTestReady(test, $.base64Decode(scriptCode));
                }
                // If there are no tests left and we aren't just running one file
                else if(testGroupIndex < testGroups.length - 1 && mode !== "one") {
                    // And if we are running multiple chapters
                    if(mode == "multiple") {
                        var i = testGroupIndex + 1;
                        testGroupIndex = -1;
                        for(; i < testGroups.length && testGroupIndex == -1; i++) {
                            if(testGroups[i].selected === true) {
                                testGroupIndex = i;
                            }
                        }
                        if(testGroupIndex == -1) { // we couldn't find a test we haven't run yet
                            loader.onTestsExhausted();
                            return;
                        }
                    }
    
    NikSurya's avatar
    NikSurya committed
                    // And if
    
                    else {
                        // We don't have tests left in this test group, so move on
                        // to the next.
                        testGroupIndex++;
                    }
                    getNextXML();
                }
    
                else {
                    // We're done.
                    loader.onTestsExhausted();
                }
            }
            else {
                presenter.updateStatus("Loading test file: " + testGroups[testGroupIndex].path);
                testGroups[testGroupIndex].onLoaded = getNextXML;
    
        /* Reset counters that track the next test (so we test from the beginning) */
    
        /* Begin downloading test files. */
        this.startLoadingTests = function() {
            loadTestXML();
        };
    
        /* Prepare for testing a single chapter. */
        this.setChapter = function(index) {
            currentTestIndex = 0;
            testGroupIndex = index;
            mode = "one";
    
            if(testGroups[index].status == 'loaded') {
                loader.onInitialized(testGroups[index].tests.length);
            }
            else {
                testGroups[index].status = 'priorityloading';
                getFile(index);
                loader.onInitialized(0);
            }
        };
    
        /* Prepare for testing multiple chapters. Returns true if at least one chapter was selected. */
        this.setMultiple = function() {
            // Find the index of the first selection
            var firstSelectedIndex = -1;
            for(var i = 0; firstSelectedIndex == -1 && i < testGroups.length; i++) {
                if(testGroups[i].selected) {
                    firstSelectedIndex = i;
                }
            }
            // If we didn't find a selected index, just quit.
            if(firstSelectedIndex == -1) {
                return false;
            }
    
            // Begin loading the file immediately, if necessary
            if(testGroups[firstSelectedIndex].status == 'waiting') {
                getFile(firstSelectedIndex);
            }
    
            mode = "multiple";
            testGroupIndex = firstSelectedIndex; // start at this chapter
            currentTestIndex = 0; // start at test 0
    
            // Count the number of tests
            runningTests = 0;
            for(var i = 0; i < testGroups.length; i++) {
                runningTests += (testGroups[i].selected && testGroups[i].status == 'loaded') ? testGroups[i].tests.length : 0;
            }
            loader.onInitialized(runningTests);
            return true;
        };
    
        this.getNumTestFiles = function() {
            return testGroups.length;
        };
    
        /* Toggle the selection of a file. */
        this.toggleSelection = function(index) {
            testGroups[index].selected = !testGroups[index].selected;
        }
    
    /* Controls test generation and running, and sends results to the presenter. */
    function Controller() {
    
        var state  = 'undefined';
    
        var runner = new BrowserRunner();
        var loader = new TestLoader();
        var controller = this;
    
    David Fugate's avatar
    David Fugate committed
        //Hook which allows browser implementers to hook their own test harness API
        //into this test framework to handle test case failures and passes in their
        //own way (e.g., logging failures to the filesystem)
        this.implementerHook = {
    
    NikSurya's avatar
    NikSurya committed
            //Adds a test result        
            addTestResult: function (test) { },            
    
    
            //Called whenever all tests have finished running.  Provided with the
    
    David Fugate's avatar
    David Fugate committed
            //elapsed time in milliseconds.
            finished: function(elapsed) { }
        };
    
        /* Executes when a test case finishes executing. */
    
        runner.onComplete = function(test) {
            presenter.addTestResult(test);
    
    David Fugate's avatar
    David Fugate committed
            try {
                controller.implementerHook.addTestResult(test);
            } catch(e) { /*no-op*/}
    
            if(state === 'running') {
    
        /* Executes when the loader has been initialized. */
    
        loader.onInitialized = function(totalTests) {
    
            if(arguments.length == 0) {
                totalTests = loader.totalTests;
            }
    
        /* Executes when a test file starts loading. */
        loader.onTestStartLoading = function(index, path) {
            presenter.setTestLoading(index, path);
        }
    
        /* Executes when a test is ready to run. */
    
    David Fugate's avatar
    David Fugate committed
        loader.onTestReady = function(testObj, testSrc) {
            presenter.updateStatus("Running Test: " + testObj.id);
    
    NikSurya's avatar
    NikSurya committed
            runner.run(testObj, testSrc);
    
        /* Executes when there are no more tests to run. */
    
            elapsed = elapsed.toFixed(3);
    
            state = (loader.loadedFiles == loader.getNumTestFiles()) ? 'loaded' : 'loading';
            presenter.setState(state);
    
    David Fugate's avatar
    David Fugate committed
            try {
                controller.implementerHook.finished(elapsed);
            } catch(e) { /*no-op*/}
    
        /* Start the test execution. */
    
            presenter.setState(state);
    
        /* Pause the test execution. */
    
            presenter.setState(state);
    
        /* Reset the testing status. */
    
            loader.onInitialized();
    
            state = (loader.loadedFiles == loader.getNumTestFiles()) ? 'loaded' : 'loading';
            presenter.setState(state);
    
        /* Start loading tests. */
        this.startLoadingTests = function() {
            state = 'loading';
            presenter.setState(state);
            loader.startLoadingTests();
        }
    
        /* Set the individual chapter in the laoder and start the controller. */
        this.runIndividualTest = function(index) {
            controller.reset();
            loader.setChapter(index);
            controller.start();
        }
    
        /* Compile a list of the selected tests and start the controller. */
        this.runSelected = function() {
            controller.reset();
            if(loader.setMultiple()) {
    
        this.runAll = function() {
            controller.reset();
            controller.start();
        }
    
        this.toggleSelection = function(index) {
            loader.toggleSelection(index);
        }
    
    NikSurya's avatar
    NikSurya committed
    
        this.testElapsedTime = function(time){
            elapsed += time;
        }
    
    var controller = new Controller();
    
    /* Helper function which shows if we're in the 'debug' mode of the Test262 site.
    
       This mode is only useful for debugging issues with the test harness and
    
        var str=window.location.href.substring(window.location.href.indexOf("?")+1);
    
        if(str.indexOf("sitedebug") > -1) {
            return true;
        }
        else {
            return false;
        }
    }
    
    
    
    $(function () {
        presenter.setup();
        $('.content-home').show();
    
        // Adding attribute to the tabs (e.g. Home, Run etc.) and
        // attaching the click event on buttons (e.g. Reset, Start etc.)
    
            //Adding "targetDiv" attribute to the header tab and on that
            //basis the div related to header tabs are displayed
    
            if (index === 0) {
                $(this).attr('targetDiv', '.content-home');
            } else if (index === 1) {
                $(this).attr('targetDiv', '.content-tests');
            } else if (index === 2) {
                $(this).attr('targetDiv', '.content-results');
                $(this).attr('testRunning', 'false');
            } else if (index === 3) {
                $(this).attr('targetDiv', '.content-dev');
            }
            else {
                $(this).attr('targetDiv', '.content-browsers');
            }
    
    
            //Attaching the click event to the header tab that shows the
            //respective div of header
    
            $(this).click(function () {
                var target = $(this).attr('targetDiv');
                $('#contentContainer > div:visible').hide();
                $('.navBar .selected').toggleClass('selected');
                $(this).addClass('selected');
                $(target).show();
    
                //If clicked tab is Result, it generates the results.
                if ($(target).hasClass('content-results')) {
                    presenter.refresh();
                }
            });
        });
    
    
        // Attach click events to all the control buttons.
        $('#btnRunAll').click(controller.runAll);
        $('#btnReset').click(controller.reset);
        $('#btnRunSelected').click(controller.runSelected);
        $('#btnPause').click(controller.pause);
        $('#btnResume').click(controller.start);
    
    
        var SUITE_DESCRIP_PATH = "json/suiteDescrip.json";
        $.ajax({ url: SUITE_DESCRIP_PATH, dataType: 'json', success: function (data) {
            presenter.setVersion(data.version);
            presenter.setDate(data.date);
        }
        });
    
        // Start loading the files right away.
        controller.startLoadingTests();