UNPKG

alloy

Version:

Appcelerator Titanium MVC Framework

214 lines (189 loc) 9.86 kB
var fs = require('fs'), path = require('path'), colors = require('colors'), TU = require('../lib/testUtils'), CONST = require('../../Alloy/common/constants'), _ = require('../../Alloy/lib/alloy/underscore')._, uglifyjs = require('uglify-js'), optimizer = require('../../Alloy/commands/compile/optimizer'); var tests = [ // make sure we didn't break normal conditionals and assigments ['var a = Ti.Platform.name', 'var a="<%= name %>"'], ['var a = Titanium.Platform.name', 'var a="<%= name %>"'], ['var a = Ti.Platform.name=="<%= name %>" ? 1 : 0', 'var a=1'], ['var a = Ti.Platform.name=="<%= name %>", b', 'var a=true,b'], ['var a = Ti.Platform.name=="<%= name %>", b, c = 2', 'var a=true,b,c=2'], ['var a = Ti.Platform.name=="<%= name %>"', 'var a=true'], ['var a = Ti.Platform.name!="iPhone OS"', 'var a=false', ['ios']], ['var a = Ti.Platform.name=="iPhone OS"', 'var a=false', notPlatform('ios')], ['var a, b = Ti.Platform.name=="<%= name %>", c = 2;', 'var a,b=true,c=2'], ['var a = "<%= name %>"==Ti.Platform.name ? 1 : 0', 'var a=1'], ['var a = "<%= name %>"==Ti.Platform.name, b', 'var a=true,b'], ['var a = "<%= name %>"==Ti.Platform.name, b, c = 2', 'var a=true,b,c=2'], ['var a = "<%= name %>"==Ti.Platform.name', 'var a=true'], ['var a = "iPhone OS"!=Ti.Platform.name', 'var a=false', ['ios']], ['var a = "iPhone OS"==Ti.Platform.name', 'var a=false', notPlatform('ios')], ['var a, b = "<%= name %>"==Ti.Platform.name, c = 2;', 'var a,b=true,c=2'], ['var a = "1"', 'var a="1"'], ['var a = true', 'var a=true'], ['var a = 1', 'var a=1'], ['var a', 'var a'], ['var a = {}', 'var a={}'], ['var a = new Object', 'var a=new Object'], ['var a = new Object()', 'var a=new Object'], ['var a = Ti.Platform.name', 'var a="<%= name %>"'], ['var a = Ti.Platform.osname', 'var a="android"', ['android']], ['var a = Ti.Platform.osname', 'var a="mobileweb"', ['mobileweb']], ['var a = Ti.Platform.osname', 'var a="blackberry"', ['blackberry']], ['var a, b = 1, c = 2;', 'var a,b=1,c=2'], ['var a = 1;', 'var a=1'], ['var a =+1;', 'var a=1'], ['var a =1+1;', 'var a=2'], ['var a = 1.0;', 'var a=1'], ['var a = 1.02;', 'var a=1.02'], ['var a = -1.02;', 'var a=-1.02'], ['var a = false', 'var a=false'], ['var a = true ? 1 : 0;', 'var a=1'], ["var num = isNaN(amount) || amount === '' || amount === null ? 0.00 : amount;", 'var num=isNaN(amount)||""===amount||null===amount?0:amount'], // TODO: Revisit all "var a,a=2;" expecteds once ALOY-540 is resolved // make sure we didn't break normal if conditions ['if (true) { var a = 1; } else { var a = 2; }', "var a,a=1"], // check platform conditionals (if/else) ["if (Titanium.Platform.name === '<%= name %>'){ var a = 1; } else { var a = 2; }","var a,a=1"], ["if (Titanium.Platform.name !== '<%= name %>'){ var a = 1; } else { var a = 2; }","var a,a=2"], ["if (Titanium.Platform['name'] == '<%= name %>'){ var a = 1; } else { var a = 2; }","var a,a=1"], ["if (Titanium.Platform.name !== '<%= name %>'){ var a = 1; } else { var a = 2; }","var a,a=2"], ["if (Titanium.Platform['name'] !== '<%= name %>'){ var a = 1; } else { var a = 2; }","var a,a=2"], // check platform conditional assignments ["var platform = Ti.Platform['name'] === '<%= name %>'", "var platform=true"], ["var platform = Ti.Platform[\"name\"] === '<%= name %>'", "var platform=true"], ["var platform = Ti.Platform.name === '<%= name %>'", "var platform=true"], ["var platform = Ti.Platform.name === 'iPhone OS'", "var platform=false", notPlatform('ios')], ["var platform = (Ti.Platform.name === '<%= name %>')", "var platform=true"], ["var platform = (Ti.Platform.name === '<%= name %>') ? 1 : 0", "var platform=1"], ["var platform = (Ti.Platform.name === '<%= name %>') ? true : false", "var platform=true"], ["var platform = (Ti.Platform.name === 'iPhone OS') ? 1 : 0", "var platform=0", notPlatform('ios')], ["var platform = (Ti.Platform.name === 'iPhone OS') ? true : false", "var platform=false", notPlatform('ios')], ["var platform = (Ti.Platform.name == '<%= name %>') ? 'true' : 'false'", "var platform=\"true\""], ["var platform = (Ti.Platform.name == 'iPhone OS') ? 'true' : 'false'", "var platform=\"false\"", notPlatform('ios')], ["var platform = (Ti.Platform.osname == 'android') ? 'true' : 'false'", "var platform=\"true\"", ['android']], ["var platform = (Ti.Platform.osname == \"iphone\") ? 1 : 0", "var platform=0", notPlatform('ios')], ["var platform = (Ti.Platform.osname == \"iphone\") ? 1 : 0", "var platform=\"iphone\"==Ti.Platform.osname?1:0", ['ios']], // check identities ["var a = Ti.Platform.name === Titanium.Platform.name","var a=true"], // shouldn't attempt to process anything other than strings ["if (Ti.Platform.name === couldBeAnything()) { var a = 1; } else { var a = 2; }","if(\"<%= name %>\"===couldBeAnything())var a=1\nelse var a=2"], ["if (Ti.Platform.name === some.Other.Value) { var a = 1; } else { var a = 2; }","if(\"<%= name %>\"===some.Other.Value)var a=1\nelse var a=2"], ["if (Ti.Platform.name !== aVariable) { var a = 1; } else { var a = 2; }","if(\"<%= name %>\"!==aVariable)var a=1\nelse var a=2"], // properly handles conditionals without curly braces ["if (Ti.Platform.name === '<%= name %>') var a = 1; else var a = 2;","var a,a=1"], ["if (Ti.Platform.name !== '<%= name %>') var a = 1; else var a = 2;","var a,a=2"], ["if ('<%= name %>' === Ti.Platform.name) var a = 1; else var a = 2;","var a,a=1"], ["if ('<%= name %>' !== Ti.Platform.name) var a = 1; else var a = 2;","var a,a=2"], // works if Ti.Platform.* is on the left or right hand side ["if ('<%= name %>' === Ti.Platform.name) { var a = 1; } else { a = 2; }","var a=1"] ]; // Prepare each platform with values we can swap out at compile time. // This means less walks over the native bridge, which means better performance. var platforms = {}; var platformsDir = path.join(__dirname,'..','..','platforms'); _.each(CONST.PLATFORMS, function(p) { platforms[p] = require(path.join(platformsDir,p,'index')); }); // The alloy command test suite describe('optimizer.js', function() { _.each(tests, function(test, index) { describe('test #' + (index+1), function() { _.each(platforms, function(platformObj, platform) { describe('[' + platform + ']', function() { var ast, code, testContent = _.template(test[0], platforms[platform]), prefix = pad(platform), defines = {}; _.each(CONST.PLATFORMS, function(p) { defines['OS_' + p.toUpperCase()] = platform === p; }); it(prefix + testContent.blue, function() { expect(true).toBe(true); }); it(prefix + 'parses AST with uglifyjs', function() { var parseFunction = function() { ast = uglifyjs.parse(testContent); }; expect(parseFunction).not.toThrow(); }); it(prefix + 'optimizes AST via optimizer.js', function() { var optimizeFunction = function() { ast.figure_out_scope(); ast = optimizer.optimize(ast, defines); }; expect(optimizeFunction).not.toThrow(); }); it(prefix + 'ast_squeeze', function() { // execute the squeeze to remove dead code, always performed // as the last step of JS file processing. The unit testing here // uses the same settings as the Alloy compile process. var squeezeFunction = function() { var options = { sequences : true, // join consecutive statemets with the “comma operator” properties : false, // optimize property access: a["foo"] → a.foo dead_code : true, // discard unreachable code drop_debugger : false, // discard “debugger” statements unsafe : false, // some unsafe optimizations (see below) conditionals : true, // optimize if-s and conditional expressions comparisons : true, // optimize comparisons evaluate : true, // evaluate constant expressions booleans : false, // optimize boolean expressions loops : false, // optimize loops unused : true, // drop unused variables/functions hoist_funs : true, // hoist function declarations hoist_vars : false, // hoist variable declarations if_return : true, // optimize if-s followed by return/continue join_vars : true, // join var declarations cascade : true, // try to cascade `right` into `left` in sequences side_effects : true, // drop side-effect-free statements warnings : false, // warn about potentially dangerous optimizations/code global_defs : defines // global definitions }; ast.figure_out_scope(); ast = ast.transform(uglifyjs.Compressor(options)); }; expect(squeezeFunction).not.toThrow(); }); it(prefix + 'generates code from optimized AST via uglifyjs', function() { var generateFunction = function() { var stream = uglifyjs.OutputStream({ beautify: false, semicolons: false, bracketize: false }); ast.print(stream); code = stream.toString().replace(/\s*$/,''); }; expect(generateFunction).not.toThrow(); }); it(prefix + 'generated code matches expected code', function() { var passFor = test[2]; var expected = _.template(test[1], platforms[platform]); if (!passFor || _.contains(passFor, platform)) { expect(code).toBe(expected); } else { expect(code).not.toBe(expected); } }); }); }); }); }); }); // helper functions function notPlatform(platform) { return _.reject(CONST.PLATFORMS, function(p) { return p === platform; } ); } function pad(string) { var ret = ''; for (var i = 0; i < 10 - string.length; i++) { ret += ' '; } return ret; }