1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview Generator script for creating gtest-style JavaScript 7 * tests for WebUI and unit tests. Generates C++ gtest wrappers 8 * which will invoke the appropriate JavaScript for each test. 9 * @author scr@chromium.org (Sheridan Rawlins) 10 * @see WebUI testing: http://goo.gl/ZWFXF 11 * @see gtest documentation: http://goo.gl/Ujj3H 12 * @see chrome/chrome_tests.gypi 13 * @see tools/gypv8sh.py 14 */ 15 16// Arguments from rules in chrome_tests.gypi are passed in through 17// python script gypv8sh.py. 18if (arguments.length < 4) { 19 print('usage: ' + 20 arguments[0] + ' path-to-testfile.js testfile.js output.cc test-type'); 21 quit(-1); 22} 23 24/** 25 * Full path to the test input file. 26 * @type {string} 27 */ 28var jsFile = arguments[1]; 29 30/** 31 * Relative path to the test input file appropriate for use in the 32 * C++ TestFixture's addLibrary method. 33 * @type {string} 34 */ 35var jsFileBase = arguments[2]; 36 37/** 38 * Path to C++ file generation is outputting to. 39 * @type {string} 40 */ 41var outputFile = arguments[3]; 42 43/** 44 * Type of this test. 45 * @type {string} ('unit'| 'webui') 46 */ 47var testType = arguments[4]; 48 49/** 50 * C++ gtest macro to use for TEST_F depending on |testType|. 51 * @type {string} ('TEST_F'|'IN_PROC_BROWSER_TEST_F') 52 */ 53var testF; 54 55/** 56 * Keeps track of whether a typedef has been generated for each test 57 * fixture. 58 * @type {Object.<string, string>} 59 */ 60var typedeffedCppFixtures = {}; 61 62/** 63 * Maintains a list of relative file paths to add to each gtest body 64 * for inclusion at runtime before running each JavaScript test. 65 * @type {Array.<string>} 66 */ 67var genIncludes = []; 68 69/** 70 * When true, add calls to set_preload_test_(fixture|name). This is needed when 71 * |testType| === 'browser' to send an injection message before the page loads, 72 * but is not required or supported for |testType| === 'unit'. 73 * @type {boolean} 74 */ 75var addSetPreloadInfo; 76 77// Generate the file to stdout. 78print('// GENERATED FILE'); 79print('// ' + arguments.join(' ')); 80print('// PLEASE DO NOT HAND EDIT!'); 81print(); 82 83// Output some C++ headers based upon the |testType|. 84// 85// Currently supports: 86// 'unit' - unit_tests harness, js2unit rule, V8UnitTest superclass. 87// 'webui' - browser_tests harness, js2webui rule, WebUIBrowserTest superclass. 88if (testType === 'unit') { 89 print('#include "chrome/test/base/v8_unit_test.h"'); 90 testing.Test.prototype.typedefCppFixture = 'V8UnitTest'; 91 testF = 'TEST_F'; 92 addSetPreloadInfo = false; 93} else { 94 print('#include "chrome/test/base/web_ui_browsertest.h"'); 95 testing.Test.prototype.typedefCppFixture = 'WebUIBrowserTest'; 96 testF = 'IN_PROC_BROWSER_TEST_F'; 97 addSetPreloadInfo = true; 98} 99print('#include "url/gurl.h"'); 100print('#include "testing/gtest/include/gtest/gtest.h"'); 101print(); 102 103/** 104 * Convert the |includeFile| to paths appropriate for immediate 105 * inclusion (path) and runtime inclusion (base). 106 * @param {string} includeFile The file to include. 107 * @return {{path: string, base: string}} Object describing the paths 108 * for |includeFile|. 109 */ 110function includeFileToPaths(includeFile) { 111 return { 112 path: jsFile.replace(/[^\/\\]+$/, includeFile), 113 base: jsFileBase.replace(/[^\/\\]+$/, includeFile), 114 }; 115} 116 117/** 118 * Output |code| verbatim. 119 * @param {string} code The code to output. 120 */ 121function GEN(code) { 122 print(code); 123} 124 125/** 126 * Generate includes for the current |jsFile| by including them 127 * immediately and at runtime. 128 * @param {Array.<string>} includes Paths to JavaScript files to 129 * include immediately and at runtime. 130 */ 131function GEN_INCLUDE(includes) { 132 for (var i = 0; i < includes.length; i++) { 133 var includePaths = includeFileToPaths(includes[i]); 134 var js = read(includePaths.path); 135 ('global', eval)(js); 136 genIncludes.push(includePaths.base); 137 } 138} 139 140/** 141 * Generate gtest-style TEST_F definitions for C++ with a body that 142 * will invoke the |testBody| for |testFixture|.|testFunction|. 143 * @param {string} testFixture The name of this test's fixture. 144 * @param {string} testFunction The name of this test's function. 145 * @param {Function} testBody The function body to execute for this test. 146 */ 147function TEST_F(testFixture, testFunction, testBody) { 148 var browsePreload = this[testFixture].prototype.browsePreload; 149 var browsePrintPreload = this[testFixture].prototype.browsePrintPreload; 150 var testGenPreamble = this[testFixture].prototype.testGenPreamble; 151 var testGenPostamble = this[testFixture].prototype.testGenPostamble; 152 var typedefCppFixture = this[testFixture].prototype.typedefCppFixture; 153 var isAsyncParam = testType === 'unit' ? '' : 154 this[testFixture].prototype.isAsync + ', '; 155 var testShouldFail = this[testFixture].prototype.testShouldFail; 156 var testPredicate = testShouldFail ? 'ASSERT_FALSE' : 'ASSERT_TRUE'; 157 var extraLibraries = genIncludes.concat( 158 this[testFixture].prototype.extraLibraries.map( 159 function(includeFile) { 160 return includeFileToPaths(includeFile).base; 161 })); 162 163 if (typedefCppFixture && !(testFixture in typedeffedCppFixtures)) { 164 print('typedef ' + typedefCppFixture + ' ' + testFixture + ';'); 165 typedeffedCppFixtures[testFixture] = typedefCppFixture; 166 } 167 168 print(testF + '(' + testFixture + ', ' + testFunction + ') {'); 169 for (var i = 0; i < extraLibraries.length; i++) { 170 print(' AddLibrary(base::FilePath(FILE_PATH_LITERAL("' + 171 extraLibraries[i].replace(/\\/g, '/') + '")));'); 172 } 173 print(' AddLibrary(base::FilePath(FILE_PATH_LITERAL("' + 174 jsFileBase.replace(/\\/g, '/') + '")));'); 175 if (addSetPreloadInfo) { 176 print(' set_preload_test_fixture("' + testFixture + '");'); 177 print(' set_preload_test_name("' + testFunction + '");'); 178 } 179 if (testGenPreamble) 180 testGenPreamble(testFixture, testFunction); 181 if (browsePreload) 182 print(' BrowsePreload(GURL("' + browsePreload + '"));'); 183 if (browsePrintPreload) { 184 print(' BrowsePrintPreload(GURL(WebUITestDataPathToURL(\n' + 185 ' FILE_PATH_LITERAL("' + browsePrintPreload + '"))));'); 186 } 187 print(' ' + testPredicate + '(RunJavascriptTestF(' + isAsyncParam + 188 '"' + testFixture + '", ' + 189 '"' + testFunction + '"));'); 190 if (testGenPostamble) 191 testGenPostamble(testFixture, testFunction); 192 print('}'); 193 print(); 194} 195 196// Now that generation functions are defined, load in |jsFile|. 197var js = read(jsFile); 198eval(js); 199