Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

New runtime (wip) and optimized/minimal amd loader

  • Loading branch information...
commit 517c3b9fbb2a834c8a6ff750f708d9cf793831d4 1 parent e1503b3
@nikhilk authored
View
1  .gitignore
@@ -5,4 +5,5 @@ obj/
*.cache
*.trx
packages/
+Tests/*/Scripts
TestResults/
View
307 src/Core/Scripts/Loader.js
@@ -0,0 +1,307 @@
+(function(global) {
+ "use strict";
+
+ // Helpers
+
+ function each(items, action) {
+ var results = [];
+ for (var i = 0, len = items.length; i < len; i++) {
+ results.push(action(items[i], i));
+ }
+ return results;
+ }
+
+ // Script management
+ // Each script has a name, and an associated url or script text.
+ // Scripts can be registered in advance using script tags on the page,
+ // either with a url:
+ // <script type="text/script" data-name="..." data-src="..."></script>
+ // or with text:
+ // <script type="text/script" data-name="...">...</script>
+ // The use of text/script ensures the script does not run immediately.
+ //
+ // If a script has not been explicitly registered, a url can be created
+ // using a convention. The current convention is simply:
+ // /scripts/<scriptname>.js.
+ //
+ // Script registration provides additional capabilities:
+ // Auto-loading: If data-autoload is set to true, then the script is
+ // automatically loaded on startup.
+ // Pre-loading: If data-preload="true" is set, then after all startup
+ // scripts have been loaded, any preloadable scripts are
+ // loaded (but not executed).
+ // Pre-loading is only supported on scripts from the same
+ // domain.
+ // Local storage: If data-store is set on a script with inline text with
+ // the version of the script, it is saved to local storage.
+ // A scripts cookie is updated to indicate the client has
+ // a copy of the script, allowing the server to generate
+ // a script tag with data-store set to 'load'. This allows
+ // inlining the script on the first request which saves
+ // roundtrips, and not paying the price of inlining on
+ // subsequent requests.
+ //
+ // TODO: Improve convention + provide ability to customize/replace it.
+ // Provide ability to plug in other types of loaders (like for css
+ // and templates).
+
+ var _xdomainRegex = /^https?:\/\/.*/;
+ var _storageCookie = !!window.localStorage ? (document.scriptCookie || 'scripts') : null;
+
+ var _scripts = {};
+ var _startupScripts = [];
+ var _deferredScripts = [];
+ function _getScript(name) {
+ var script = _scripts[name];
+ if (!script) {
+ // Create a script object with a url based on convention
+ script = _scripts[name] = { name: name, src: '/scripts/' + name + '.js' };
+ }
+ return script;
+ }
+ function _loadScriptConfiguration() {
+ var scriptElements = document.getElementsByTagName('script');
+ for (var i = 0, count = scriptElements.length; i < count; i++) {
+ var scriptElement = scriptElements[i];
+ if (scriptElement.type == 'text/script') {
+ var name = scriptElement.getAttribute('data-name');
+ var inline = false;
+ if (!name) {
+ // If there was no name set, assume this script has inline text,
+ // generate a name, so it can be managed, and include it as a
+ // startup script that is auto-loaded.
+
+ name = 's' + (new Date()).valueOf();
+ inline = true;
+ }
+
+ var script = _scripts[name] = {
+ name: name,
+ src: scriptElement.getAttribute('data-src')
+ };
+
+ if (!script.src) {
+ // No src was specified, so use script text instead.
+ var text;
+
+ var storage = _storageCookie ? scriptElement.getAttribute('data-store') : null;
+ if (storage == 'load') {
+ // Storage was set to load, so load script text from local storage.
+ text = _loadLocalScript(name);
+ }
+ else {
+ // Use the inlined script text.
+ text = scriptElement.text;
+ if (storage) {
+ // Storage was specified, so save the text to local storage for use
+ // on a subsequent page load, and use the value of the storage attribute
+ // as the version marker to communicate to the server that we
+ // have a particular script in storage.
+
+ _saveLocalScript(name, text, storage);
+ }
+ }
+
+ if (inline) {
+ // Assumption is if the name wasn't specified on the script tag, then
+ // it wasn't specified in the define call either. Patch up the script
+ // text to use the auto-generated module name for the module name
+ // specified in the call to define (so that the callbacks associated
+ // with this module get invoked when the script executes).
+
+ text = text.replace('define([', 'define(\'' + script.name + '\', [');
+ }
+ script.text = text;
+ script.useText = true;
+ }
+
+ if (inline || scriptElement.hasAttribute('data-autoload')) {
+ _startupScripts.push(script.name);
+ }
+ if (!_xdomainRegex.test(script.src) && scriptElement.hasAttribute('data-preload')) {
+ _deferredScripts.push(script.name);
+ }
+ }
+ }
+ }
+ function _loadLocalScript(name) {
+ return localStorage['script.' + name];
+ }
+ function _saveLocalScript(name, text, version) {
+ localStorage['script.' + name] = text;
+
+ // Save the fact that we have stored this script into the local storage
+ // by updating the index with the new script information, and using
+ // the index as the value of the cookie sent to the server as well.
+ var scriptIndex = localStorage['script.$'];
+
+ scriptIndex = scriptIndex ? JSON.parse(scriptIndex) : {};
+ scriptIndex[name] = version;
+
+ scriptIndex = JSON.stringify(scriptIndex);
+ localStorage['script.$'] = scriptIndex;
+
+ // A cookie with 180 days = 60 * 60 * 24 * 180 seconds
+ document.cookie = _storageCookie + '=' + encodeURIComponent(scriptIndex) +
+ '; max-age=15552000; path=/';
+ }
+ function _downloadScript(name) {
+ var script = _scripts[name];
+
+ var downloader = new XMLHttpRequest();
+ downloader.onreadystatechange = function() {
+ if (downloader.readyState == 4) {
+ downloader.onreadystatechange = null;
+
+ if (downloader.status == 200) {
+ script.useText = true;
+ script.text = downloader.responseText;
+ }
+ }
+ };
+ downloader.open('GET', script.src, true);
+ downloader.send();
+ }
+
+
+ // Script loading
+ // Scripts are loaded by creating script elements and inserting
+ // into the document's head element.
+ // Scripts can either be loaded from a url (using the src attribute)
+ // or loaded from previously loaded script text (using the text property).
+ //
+ // The assumption is the script uses the AMD pattern, so doesn't need
+ // to be tracked - the call to define will indicate the script has been
+ // loaded and executed.
+ //
+ // TODO: Provide ability to plug in a shim for scripts that don't follow
+ // the AMD pattern.
+
+ function _loadScript(scriptName) {
+ var script = _getScript(scriptName);
+
+ var scriptElement = document.createElement('script');
+ scriptElement.type = 'text/javascript';
+ if (script.useText) {
+ scriptElement.text = script.text;
+ }
+ else {
+ scriptElement.src = script.src;
+ }
+ document.getElementsByTagName('head')[0].appendChild(scriptElement);
+ }
+
+
+ // Module management
+
+ var _modules = {};
+ function _getModule(name) {
+ return _modules[name] ||
+ (_modules[name] = { name: name, exports: null, callbacks: [], loading: false });
+ }
+ function _loadModule(name, callback) {
+ var module = _getModule(name);
+
+ if (module.callbacks) {
+ // Module hasn't loaded yet, since we're still hanging on
+ // to dependents, so add this callback to the list.
+
+ module.callbacks.push(callback);
+
+ if (!module.loading) {
+ // Kick off loading the module if this is the first dependent.
+ module.loading = true;
+ _loadScript(name);
+ }
+
+ return 0;
+ }
+
+ // Module is already loaded, so invoke the callback but use a
+ // setTimeout to mimic how the callback is invoked asynchronously
+ // in the usual case when the module hasn't already been loaded.
+ return setTimeout(callback, 0);
+ }
+ function _completeModule(name, exports) {
+ var module = _modules[name];
+
+ var callbacks = module.callbacks;
+ module.callbacks = null;
+
+ module.exports = exports;
+ module.loading = false;
+
+ each(callbacks, function(callback) {
+ callback();
+ });
+ }
+
+
+ // Exported APIs
+ // As per the AMD pattern, we're exporting require and define.
+ // Our version of define only works with explicitly specified
+ // module names (which is anyway true if individual modules are
+ // combined into a single script on the server).
+
+ function require(dependencyNames, callback) {
+ dependencyNames = dependencyNames || [];
+
+ var dependencyCount = dependencyNames.length;
+ if (dependencyCount == 0) {
+ callback.apply(global);
+ return;
+ }
+
+ var dependenciesAvailable = 0;
+ var localCallback = function() {
+ dependenciesAvailable++;
+ if (dependenciesAvailable == dependencyCount) {
+ var dependencies = each(dependencyNames, function(dependencyName) {
+ return _getModule(dependencyName).exports;
+ });
+
+ callback.apply(global, dependencies);
+ }
+ };
+
+ each(dependencyNames, function(dependencyName) {
+ _loadModule(dependencyName, localCallback);
+ });
+ }
+
+ global.require = require;
+ global.define = function(name, dependencyNames, callback) {
+ require(dependencyNames, function() {
+ _completeModule(name, callback.apply(global, arguments));
+ });
+ }
+
+
+ // Bootstrapping
+ // If the document has already loaded (i.e. when this script was
+ // dynamically added to the DOM), go ahead and run startup immediately.
+ // Otherwise hookup to the DOMContentLoaded event on the document
+ // which is when the DOM is ready for programmatic access, or if
+ // this is an older browser, simply do the next best thing - wait for
+ // the page to be loaded completely.
+
+ function _startup() {
+ _loadScriptConfiguration();
+ require(_startupScripts, function(startupObjects) {
+ each(_deferredScripts, function(name) {
+ _downloadScript(name);
+ });
+ });
+ }
+
+ if (document.addEventListener) {
+ document.readyState == 'complete' ?
+ _startup() :
+ document.addEventListener('DOMContentLoaded', _startup, false);
+ }
+ else if (window.attachEvent) {
+ window.attachEvent('onload', function() {
+ _startup();
+ });
+ }
+})(window);
View
15 src/Core/Scripts/Runtime.js
@@ -5,6 +5,8 @@
define('ss', [], function() {
"use strict";
+var global = this;
+
// TODO: Inline and remove
function isUndefined(o) {
return (o === undefined);
@@ -43,7 +45,18 @@ return extend(ss, {
isNullOrUndefined: isNullOrUndefined,
isValue: isValue,
- module: module
+ module: module,
+ isClass: isClass,
+ isInterface: isInterface,
+ getType: getType,
+ baseType: getBaseType,
+ interfaces: getInterfaces,
+ canCast: canCast,
+ safeCast: safeCast,
+ canAssign: canAssign,
+ isOfType: isOfType,
+ typeName: getTypeName,
+ type: parseType
});
});
View
97 src/Core/Scripts/Runtime/TypeSystem.js
@@ -38,12 +38,109 @@ function createType(typeName, typeInfo, typeRegistry) {
type.$type = _interfaceMarker;
}
+ type.$name = typeName;
return typeRegistry[typeName] = type;
}
return typeInfo;
}
+function isClass(fn) {
+ return fn.$type == _classMarker;
+}
+
+function isInterface(fn) {
+ return fn.$type == _interfaceMarker;
+}
+
+function getType(instance) {
+ var ctor = null;
+
+ // NOTE: We have to catch exceptions because the constructor
+ // cannot be looked up on native COM objects
+ try {
+ ctor = instance.constructor;
+ }
+ catch (ex) {
+ }
+ if (!ctor || !ctor.$type) {
+ ctor = Object;
+ }
+ return ctor;
+}
+
+function getBaseType(type) {
+ return type.$base || Object;
+}
+
+function getInterfaces(type) {
+ return type.$interfaces || [];
+}
+
+function canAssign(type, otherType) {
+ // Checks if the specified type is equal to otherType,
+ // or is a parent of otherType
+
+ if ((type == Object) || (type == otherType)) {
+ return true;
+ }
+ if (type.$type == _classMarker) {
+ var baseType = otherType.$base;
+ while (baseType) {
+ if (type == baseType) {
+ return true;
+ }
+ baseType = baseType.$base;
+ }
+ }
+ else if (type.$type == _interfaceMarker) {
+ var baseType = otherType;
+ while (baseType) {
+ var interfaces = baseType.$interfaces;
+ if (interfaces && (interfaces.indexOf(type) >= 0)) {
+ return true;
+ }
+ baseType = baseType.$base;
+ }
+ }
+ return false;
+}
+
+function isOfType(instance, type) {
+ // Checks if the specified instance is of the specified type
+
+ if (!isValue(instance)) {
+ return false;
+ }
+
+ if ((type == Object) || (instance instanceof type)) {
+ return true;
+ }
+
+ var instanceType = getType(instance);
+ return canAssign(type, instanceType);
+}
+
+function canCast(instance, type) {
+ return isOfType(instance, type);
+}
+
+function safeCast(instance, type) {
+ return isOfType(instance, type) ? instance : null;
+}
+
+function getTypeName(type) {
+ return type.$name;
+}
+
+function parseType(s) {
+ var nsIndex = s.indexOf('.');
+ var ns = nsIndex > 0 ? _modules[s.substr(0, nsIndex)] : global;
+ var name = nsIndex > 0 ? s.substr(nsIndex + 1) : s;
+
+ return ns ? ns[name] : null;
+}
+
function module(name, implementation, exports) {
var registry = _modules[name] = {};
View
9 src/Core/Scripts/Scripts.csproj
@@ -15,14 +15,21 @@
<OutputPath>..\..\..\bin\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
+ <Content Include="Loader.js" />
+ <Content Include="Runtime.js" />
+ </ItemGroup>
+ <ItemGroup>
<Content Include="Runtime\Console.js" />
<Content Include="Runtime\TypeSystem.js" />
- <Content Include="Runtime.js" />
</ItemGroup>
<Target Name="Build">
<Exec Command="$(ToolsPath)\sspp.exe /nologo /debug /d:DEBUG /crlf /input:$(ProjectDir)Runtime.js /output:$(OutputPath)\ss.debug.js" />
<Exec Command="$(ToolsPath)\sspp.exe /nologo /stripCommentsOnly /crlf /input:$(ProjectDir)Runtime.js /output:$(OutputPath)\ss.js" />
<Exec Command="$(ToolsPath)\ajaxmin.exe -hc -clobber:true $(OutputPath)\ss.js -out $(OutputPath)\ss.min.js" />
+
+ <Exec Command="$(ToolsPath)\sspp.exe /nologo /debug /d:DEBUG /crlf /input:$(ProjectDir)Loader.js /output:$(OutputPath)\ssloader.debug.js" />
+ <Exec Command="$(ToolsPath)\sspp.exe /nologo /stripCommentsOnly /crlf /input:$(ProjectDir)Loader.js /output:$(OutputPath)\ssloader.js" />
+ <Exec Command="$(ToolsPath)\ajaxmin.exe -hc -clobber:true $(OutputPath)\ssloader.js -out $(OutputPath)\ssloader.min.js" />
</Target>
<Target Name="Clean">
</Target>
View
75 tests/Runtime/Core/Compilation.cs
@@ -0,0 +1,75 @@
+// Compilation.cs
+// Script#/Tests/Runtime
+// This source code is subject to terms and conditions of the Apache License, Version 2.0.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using ScriptSharp;
+
+namespace Runtime.Tests.Core {
+
+ public sealed class Compilation : IErrorHandler {
+
+ private List<string> _references;
+ private List<IStreamSource> _sources;
+ private IStreamSource _output;
+
+ private CompilerOptions _options;
+
+ private List<string> _errors;
+
+ public Compilation(string outputPath) {
+ _references = new List<string>();
+ _sources = new List<IStreamSource>();
+ _output = new FileStreamSource(outputPath, writable: true);
+
+ _options = new CompilerOptions();
+ _options.DebugFlavor = true;
+ _options.Minimize = false;
+ }
+
+ public bool HasErrors {
+ get {
+ return _errors.Count != 0;
+ }
+ }
+
+ public Compilation AddReference(string path) {
+ _references.Add(path);
+ return this;
+ }
+
+ public Compilation AddSource(string path) {
+ FileStreamSource sourceInput = new FileStreamSource(path, writable: false);
+ _sources.Add(sourceInput);
+
+ return this;
+ }
+
+ public bool Execute() {
+ _options.References = _references;
+ _options.Sources = _sources.Cast<IStreamSource>().ToList();
+ _options.ScriptFile = _output;
+
+ ScriptCompiler compiler = new ScriptCompiler(this);
+ return compiler.Compile(_options);
+ }
+
+ #region Implementation of IErrorHandler
+
+ void IErrorHandler.ReportError(string errorMessage, string location) {
+ if (_errors == null) {
+ _errors = new List<string>();
+ }
+
+ string error = errorMessage + " " + location;
+ _errors.Add(error);
+ }
+
+ #endregion
+ }
+}
View
51 tests/Runtime/Core/FileStreamSource.cs
@@ -0,0 +1,51 @@
+// FileStreamSource.cs
+// Script#/Tests/Runtime
+// This source code is subject to terms and conditions of the Apache License, Version 2.0.
+//
+
+using System;
+using System.IO;
+using ScriptSharp;
+
+namespace Runtime.Tests.Core {
+
+ public sealed class FileStreamSource : IStreamSource {
+
+ private string _path;
+ private bool _writable;
+
+ public FileStreamSource(string path, bool writable) {
+ _path = path;
+ _writable = writable;
+ }
+
+ #region Implementation of IStreamSource
+
+ string IStreamSource.FullName {
+ get {
+ return _path;
+ }
+ }
+
+ string IStreamSource.Name {
+ get {
+ return Path.GetFileNameWithoutExtension(_path);
+ }
+ }
+
+ void IStreamSource.CloseStream(Stream stream) {
+ stream.Close();
+ }
+
+ Stream IStreamSource.GetStream() {
+ if (_writable) {
+ return new FileStream(_path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
+ }
+ else {
+ return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.Read);
+ }
+ }
+
+ #endregion
+ }
+}
View
73 tests/Runtime/Core/RuntimeTest.cs
@@ -0,0 +1,73 @@
+// RuntimeTest.cs
+// Script#/Tests/Runtime
+// This source code is subject to terms and conditions of the Apache License, Version 2.0.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using ScriptSharp.Testing;
+
+namespace Runtime.Tests.Core {
+
+ public abstract class RuntimeTest {
+
+ private static readonly WebTest _webTest;
+ private static readonly string[] _scripts = new string[] {
+ "ss.debug.js", "ssloader.debug.js"
+ };
+ private static readonly string[] _codeFiles = new string[] {
+ "OOP.cs"
+ };
+
+ private const int _port = 3976;
+
+ private TestContext _context;
+
+ static RuntimeTest() {
+ string assemblyPath = typeof(RuntimeTest).Assembly.GetModules()[0].FullyQualifiedName;
+ string binDirectory = Path.GetFullPath(Path.Combine(assemblyPath, "..\\..\\..\\..\\..\\bin\\Debug\\"));
+
+ string webRoot = Path.GetFullPath(Path.Combine(assemblyPath, "..\\..\\..\\..\\TestSite\\"));
+ string scriptsDirectory = Path.Combine(webRoot, "Scripts");
+ string codeDirectory = Path.Combine(webRoot, "Code");
+
+ Directory.CreateDirectory(scriptsDirectory);
+ foreach (string script in _scripts) {
+ File.Copy(Path.Combine(binDirectory, script), Path.Combine(scriptsDirectory, script), overwrite: true);
+ }
+
+ string mscorlibPath = Path.Combine(binDirectory, "mscorlib.dll");
+ foreach (string codeFile in _codeFiles) {
+ string script = Path.GetFileNameWithoutExtension(codeFile) + Path.ChangeExtension(".cs", ".js");
+
+ Compilation compilation = new Compilation(Path.Combine(scriptsDirectory, script));
+ compilation.AddReference(mscorlibPath)
+ .AddSource(Path.Combine(codeDirectory, codeFile))
+ .Execute();
+ }
+
+ _webTest = new WebTest();
+ _webTest.StartWebServer(webRoot, _port);
+ }
+
+ public TestContext TestContext {
+ get {
+ return _context;
+ }
+ set {
+ _context = value;
+ }
+ }
+
+ protected void RunTest(string url) {
+ Uri testUri = _webTest.GetTestUri(url);
+
+ WebTestResult result = _webTest.RunTest(testUri, WebBrowser.Chrome);
+ Assert.IsTrue(result.Succeeded, "Log:\r\n" + result.Log);
+ }
+ }
+}
View
20 tests/Runtime/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+// AssemblyInfo.cs
+// Script#/Tests/Runtime
+// This source code is subject to terms and conditions of the Apache License, Version 2.0.
+//
+
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Runtime.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Script#")]
+[assembly: AssemblyCopyright("Copyright © Script# 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
95 tests/Runtime/Runtime.csproj
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Runtime.Tests</RootNamespace>
+ <AssemblyName>Runtime.Tests</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+ <IsCodedUITest>False</IsCodedUITest>
+ <TestProjectType>UnitTest</TestProjectType>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="ScriptSharp">
+ <HintPath>..\..\bin\Debug\ScriptSharp.dll</HintPath>
+ </Reference>
+ <Reference Include="ScriptSharp.Testing">
+ <HintPath>..\..\bin\Debug\ScriptSharp.Testing.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ </ItemGroup>
+ <Choose>
+ <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
+ <ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ </ItemGroup>
+ </When>
+ <Otherwise>
+ <ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
+ </ItemGroup>
+ </Otherwise>
+ </Choose>
+ <ItemGroup>
+ <Compile Include="Core\Compilation.cs" />
+ <Compile Include="Core\FileStreamSource.cs" />
+ <Compile Include="Core\RuntimeTest.cs" />
+ <Compile Include="TypeSystemTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Choose>
+ <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
+ <ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ </When>
+ </Choose>
+ <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
20 tests/Runtime/TypeSystemTests.cs
@@ -0,0 +1,20 @@
+// TypeSystemTests.cs
+// Script#/Tests/Runtime
+// This source code is subject to terms and conditions of the Apache License, Version 2.0.
+//
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Runtime.Tests.Core;
+
+namespace Runtime.Tests {
+
+ [TestClass]
+ public class TypeSystemTests : RuntimeTest {
+
+ [TestMethod]
+ public void TestOOP() {
+ RunTest("/TestOOP.htm");
+ }
+ }
+}
View
36 tests/ScriptSharpTests.sln
@@ -1,33 +1,15 @@

-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptSharp", "ScriptSharp\ScriptSharp.csproj", "{0E3485F3-D66D-4907-BDB9-D61D34F97538}"
EndProject
-Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "CoreLib", "CoreLib\", "{956971CD-A3AA-4C69-BF6E-A42A580B08CB}"
- ProjectSection(WebsiteProperties) = preProject
- TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0"
- Debug.AspNetCompiler.VirtualPath = "/CoreLib"
- Debug.AspNetCompiler.PhysicalPath = "CoreLib\"
- Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\CoreLib\"
- Debug.AspNetCompiler.Updateable = "true"
- Debug.AspNetCompiler.ForceOverwrite = "true"
- Debug.AspNetCompiler.FixedNames = "false"
- Debug.AspNetCompiler.Debug = "True"
- Release.AspNetCompiler.VirtualPath = "/CoreLib"
- Release.AspNetCompiler.PhysicalPath = "CoreLib\"
- Release.AspNetCompiler.TargetPath = "PrecompiledWeb\CoreLib\"
- Release.AspNetCompiler.Updateable = "true"
- Release.AspNetCompiler.ForceOverwrite = "true"
- Release.AspNetCompiler.FixedNames = "false"
- Release.AspNetCompiler.Debug = "False"
- VWDPort = "23597"
- EndProjectSection
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{37D238E7-539B-497A-98D7-760DAFCF6527}"
ProjectSection(SolutionItems) = preProject
- ScriptSharp.testsettings = ScriptSharp.testsettings
+ Tests.testsettings = Tests.testsettings
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Runtime", "Runtime\Runtime.csproj", "{B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -38,10 +20,10 @@ Global
{0E3485F3-D66D-4907-BDB9-D61D34F97538}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E3485F3-D66D-4907-BDB9-D61D34F97538}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E3485F3-D66D-4907-BDB9-D61D34F97538}.Release|Any CPU.Build.0 = Release|Any CPU
- {956971CD-A3AA-4C69-BF6E-A42A580B08CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {956971CD-A3AA-4C69-BF6E-A42A580B08CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {956971CD-A3AA-4C69-BF6E-A42A580B08CB}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {956971CD-A3AA-4C69-BF6E-A42A580B08CB}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3055BB4-FB7A-4DDD-9D8D-BC8A405514AD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
View
106 tests/TestSite/Code/OOP.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.CompilerServices;
+using Test;
+
+[assembly:ScriptAssembly("oop")]
+
+namespace Test {
+
+ public interface IMammal {
+ }
+
+ public interface IPet {
+
+ string Name {
+ get;
+ }
+
+ string Owner {
+ get;
+ }
+ }
+
+ public class Animal {
+
+ private string _species;
+
+ public Animal(string species) {
+ _species = species;
+ }
+
+ public string Species {
+ get {
+ return _species;
+ }
+ }
+ }
+
+ public class Cat : Animal, IMammal {
+
+ public Cat() : base("Cat") {
+ }
+
+ public virtual string Speak() {
+ return "meow";
+ }
+ }
+}
+
+namespace Test.More {
+
+ public interface ICharacter {
+ }
+
+ public class Garfield : Cat, IPet, ICharacter {
+
+ public string Name {
+ get {
+ return "Garfield";
+ }
+ }
+
+ public string Owner {
+ get {
+ return "Jon";
+ }
+ }
+
+ public override string Speak() {
+ return base.Speak() + "\r\n" +
+ "Translation: " +
+ "I am fat, lazy, and cynical, but still, a favorite cat...";
+ }
+ }
+
+ public class Comic {
+
+ private string _name;
+ private ICharacter _star;
+
+ public Comic(string name, ICharacter star) {
+ _name = name;
+ _star = star;
+ }
+
+ public string Name {
+ get {
+ return _name;
+ }
+ }
+
+ public ICharacter Star {
+ get {
+ return _star;
+ }
+ }
+ }
+}
+
+namespace Test.Misc {
+
+ public interface IObject {
+ }
+
+ public class Zoo {
+ }
+}
View
112 tests/TestSite/QUnit.css
@@ -0,0 +1,112 @@
+ol#qunit-tests {
+ font-family:Calibri, Helvetica, Arial;
+ margin:0;
+ padding:0;
+ list-style-position:inside;
+ font-size: smaller;
+}
+ol#qunit-tests li{
+ padding:0.4em 0.5em 0.4em 2.5em;
+ border-bottom:1px solid #fff;
+ font-size:small;
+ list-style-position:inside;
+}
+ol#qunit-tests li ol{
+ box-shadow: inset 0px 2px 13px #999;
+ -moz-box-shadow: inset 0px 2px 13px #999;
+ -webkit-box-shadow: inset 0px 2px 13px #999;
+ margin-top:0.5em;
+ margin-left:0;
+ padding:0.5em;
+ background-color:#fff;
+}
+ol#qunit-tests li li{
+ border-bottom:none;
+ margin:0.5em;
+ background-color:#fff;
+ list-style-position: inside;
+ padding:0.4em 0.5em 0.4em 0.5em;
+}
+ol#qunit-tests li li.pass{
+ border-left:26px solid #C6E746;
+ background-color:#fff;
+ color:#5E740B;
+}
+ol#qunit-tests li li.fail{
+ border-left:26px solid #EE5757;
+ background-color:#fff;
+ color:#710909;
+}
+ol#qunit-tests li.pass{
+ background-color:#D2E0E6;
+ color:#528CE0;
+}
+ol#qunit-tests li.fail{
+ background-color:#EE5757;
+ color:#000;
+}
+ol#qunit-tests li strong {
+ cursor:pointer;
+}
+h1#qunit-header{
+ background-color:#0d3349;
+ margin:0;
+ padding:0.5em 0 0.5em 1em;
+ color:#fff;
+ font-family:Calibri, Helvetica, Arial;
+ border-top-right-radius:15px;
+ border-top-left-radius:15px;
+ -moz-border-radius-topright:15px;
+ -moz-border-radius-topleft:15px;
+ -webkit-border-top-right-radius:15px;
+ -webkit-border-top-left-radius:15px;
+ text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
+}
+h2#qunit-banner{
+ font-family:Calibri, Helvetica, Arial;
+ height:5px;
+ margin:0;
+ padding:0;
+}
+h2#qunit-banner.qunit-pass{
+ background-color:#C6E746;
+}
+h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
+ background-color:#EE5757;
+}
+#qunit-testrunner-toolbar {
+ font-family:Calibri, Helvetica, Arial;
+ padding:0;
+ padding:0em 0 0.5em 2em;
+ font-size: small;
+}
+h2#qunit-userAgent {
+ font-family:Calibri, Helvetica, Arial;
+ background-color:#2b81af;
+ margin:0;
+ padding:0;
+ color:#fff;
+ font-size: small;
+ padding:0.5em 0 0.5em 2.5em;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+p#qunit-testresult{
+ font-family:Calibri, Helvetica, Arial;
+ margin:0;
+ font-size: small;
+ color:#2b81af;
+ border-bottom-right-radius:15px;
+ border-bottom-left-radius:15px;
+ -moz-border-radius-bottomright:15px;
+ -moz-border-radius-bottomleft:15px;
+ -webkit-border-bottom-right-radius:15px;
+ -webkit-border-bottom-left-radius:15px;
+ background-color:#D2E0E6;
+ padding:0.5em 0.5em 0.5em 2.5em;
+}
+strong b.fail{
+ color:#710909;
+}
+strong b.pass{
+ color:#5E740B;
+}
View
1,043 tests/TestSite/QUnit.js
@@ -0,0 +1,1043 @@
+/*
+ * QUnit - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2009 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ */
+
+(function(window) {
+
+var QUnit = {
+
+ // Initialize the configuration options
+ init: function() {
+ config = {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date,
+ blocking: false,
+ autorun: false,
+ assertions: [],
+ filters: [],
+ queue: []
+ };
+
+ var tests = id("qunit-tests"),
+ banner = id("qunit-banner"),
+ result = id("qunit-testresult");
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+ },
+
+ // call on start of module test to prepend name to all tests
+ module: function(name, testEnvironment) {
+ config.currentModule = name;
+
+ synchronize(function() {
+ if ( config.currentModule ) {
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+ }
+
+ config.currentModule = name;
+ config.moduleTestEnvironment = testEnvironment;
+ config.moduleStats = { all: 0, bad: 0 };
+
+ QUnit.moduleStart( name, testEnvironment );
+ });
+ },
+
+ asyncTest: function(testName, expected, callback) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = 0;
+ }
+
+ QUnit.test(testName, expected, callback, true);
+ },
+
+ test: function(testName, expected, callback, async) {
+ var name = testName, testEnvironment, testEnvironmentArg;
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+ // is 2nd argument a testEnvironment?
+ if ( expected && typeof expected === 'object') {
+ testEnvironmentArg = expected;
+ expected = null;
+ }
+
+ if ( config.currentModule ) {
+ name = config.currentModule + " module: " + name;
+ }
+
+ if ( !validTest(name) ) {
+ return;
+ }
+
+ synchronize(function() {
+ QUnit.testStart( testName );
+
+ testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, config.moduleTestEnvironment);
+ if (testEnvironmentArg) {
+ extend(testEnvironment,testEnvironmentArg);
+ }
+
+ // allow utility functions to access the current test environment
+ QUnit.current_testEnvironment = testEnvironment;
+
+ config.assertions = [];
+ config.expected = expected;
+
+ try {
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+
+ testEnvironment.setup.call(testEnvironment);
+ } catch(e) {
+ QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
+ }
+
+ if ( async ) {
+ QUnit.stop();
+ }
+
+ try {
+ callback.call(testEnvironment);
+ } catch(e) {
+ fail("Test " + name + " died, exception and test follows", e, callback);
+ QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ start();
+ }
+ }
+ });
+
+ synchronize(function() {
+ try {
+ checkPollution();
+ testEnvironment.teardown.call(testEnvironment);
+ } catch(e) {
+ QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
+ }
+
+ try {
+ QUnit.reset();
+ } catch(e) {
+ fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
+ }
+
+ if ( config.expected && config.expected != config.assertions.length ) {
+ QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
+ }
+
+ var good = 0, bad = 0,
+ tests = id("qunit-tests");
+
+ config.stats.all += config.assertions.length;
+ config.moduleStats.all += config.assertions.length;
+
+ if ( tests ) {
+ var ol = document.createElement("ol");
+ ol.style.display = "none";
+
+ for ( var i = 0; i < config.assertions.length; i++ ) {
+ var assertion = config.assertions[i];
+
+ var li = document.createElement("li");
+ li.className = assertion.result ? "pass" : "fail";
+ li.appendChild(document.createTextNode(assertion.message || "(no message)"));
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ var b = document.createElement("strong");
+ b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
+
+ addEvent(b, "click", function() {
+ var next = b.nextSibling, display = next.style.display;
+ next.style.display = display === "none" ? "block" : "none";
+ });
+
+ addEvent(b, "dblclick", function(e) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() === "strong" ) {
+ var text = "", node = target.firstChild;
+
+ while ( node.nodeType === 3 ) {
+ text += node.nodeValue;
+ node = node.nextSibling;
+ }
+
+ text = text.replace(/(^\s*|\s*$)/g, "");
+
+ if ( window.location ) {
+ window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
+ }
+ }
+ });
+
+ var li = document.createElement("li");
+ li.className = bad ? "fail" : "pass";
+ li.appendChild( b );
+ li.appendChild( ol );
+ tests.appendChild( li );
+
+ if ( bad ) {
+ var toolbar = id("qunit-testrunner-toolbar");
+ if ( toolbar ) {
+ toolbar.style.display = "block";
+ id("qunit-filter-pass").disabled = null;
+ id("qunit-filter-missing").disabled = null;
+ }
+ }
+
+ } else {
+ for ( var i = 0; i < config.assertions.length; i++ ) {
+ if ( !config.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ QUnit.testDone( testName, bad, config.assertions.length );
+
+ if ( !window.setTimeout && !config.queue.length ) {
+ done();
+ }
+ });
+
+ if ( window.setTimeout && !config.doneTimer ) {
+ config.doneTimer = window.setTimeout(function(){
+ if ( !config.queue.length ) {
+ done();
+ } else {
+ synchronize( done );
+ }
+ }, 13);
+ }
+ },
+
+ /**
+ * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+ */
+ expect: function(asserts) {
+ config.expected = asserts;
+ },
+
+ /**
+ * Asserts true.
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function(a, msg) {
+ QUnit.log(a, msg);
+
+ config.assertions.push({
+ result: !!a,
+ message: msg
+ });
+ },
+
+ /**
+ * Checks that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ *
+ * Prefered to ok( actual == expected, message )
+ *
+ * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
+ *
+ * @param Object actual
+ * @param Object expected
+ * @param String message (optional)
+ */
+ equal: function(actual, expected, message) {
+ push(expected == actual, actual, expected, message);
+ },
+
+ notEqual: function(actual, expected, message) {
+ push(expected != actual, actual, expected, message);
+ },
+
+ deepEqual: function(a, b, message) {
+ push(QUnit.equiv(a, b), a, b, message);
+ },
+
+ notDeepEqual: function(a, b, message) {
+ push(!QUnit.equiv(a, b), a, b, message);
+ },
+
+ strictEqual: function(actual, expected, message) {
+ push(expected === actual, actual, expected, message);
+ },
+
+ notStrictEqual: function(actual, expected, message) {
+ push(expected !== actual, actual, expected, message);
+ },
+
+ start: function() {
+ // A slight delay, to avoid any current callbacks
+ if ( window.setTimeout ) {
+ window.setTimeout(function() {
+ if ( config.timeout ) {
+ clearTimeout(config.timeout);
+ }
+
+ config.blocking = false;
+ process();
+ }, 13);
+ } else {
+ config.blocking = false;
+ process();
+ }
+ },
+
+ stop: function(timeout) {
+ config.blocking = true;
+
+ if ( timeout && window.setTimeout ) {
+ config.timeout = window.setTimeout(function() {
+ QUnit.ok( false, "Test timed out" );
+ QUnit.start();
+ }, timeout);
+ }
+ },
+
+ /**
+ * Resets the test setup. Useful for tests that modify the DOM.
+ */
+ reset: function() {
+ if ( window.jQuery ) {
+ jQuery("#main").html( config.fixture );
+ jQuery.event.global = {};
+ jQuery.ajaxSettings = extend({}, config.ajaxSettings);
+ }
+ },
+
+ /**
+ * Trigger an event on an element.
+ *
+ * @example triggerEvent( document.body, "click" );
+ *
+ * @param DOMElement elem
+ * @param String type
+ */
+ triggerEvent: function( elem, type, event ) {
+ if ( document.createEvent ) {
+ event = document.createEvent("MouseEvents");
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent( event );
+
+ } else if ( elem.fireEvent ) {
+ elem.fireEvent("on"+type);
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
+ },
+
+ // Logging callbacks
+ done: function(failures, total) {},
+ log: function(result, message) {},
+ testStart: function(name) {},
+ testDone: function(name, failures, total) {},
+ moduleStart: function(name, testEnvironment) {},
+ moduleDone: function(name, failures, total) {}
+};
+
+// Backwards compatibility, deprecated
+QUnit.equals = QUnit.equal;
+QUnit.same = QUnit.deepEqual;
+
+// Maintain internal state
+var config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true
+};
+
+// Load paramaters
+(function() {
+ var location = window.location || { search: "", protocol: "file:" },
+ GETParams = location.search.slice(1).split('&');
+
+ for ( var i = 0; i < GETParams.length; i++ ) {
+ GETParams[i] = decodeURIComponent( GETParams[i] );
+ if ( GETParams[i] === "noglobals" ) {
+ GETParams.splice( i, 1 );
+ i--;
+ config.noglobals = true;
+ } else if ( GETParams[i].search('=') > -1 ) {
+ GETParams.splice( i, 1 );
+ i--;
+ }
+ }
+
+ // restrict modules/tests by get parameters
+ config.filters = GETParams;
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = !!(location.protocol === 'file:');
+})();
+
+// Expose the API as global variables, unless an 'exports'
+// object exists, in that case we assume we're in CommonJS
+if ( typeof exports === "undefined" || typeof require === "undefined" ) {
+ extend(window, QUnit);
+ window.QUnit = QUnit;
+} else {
+ extend(exports, QUnit);
+ exports.QUnit = QUnit;
+}
+
+if ( typeof document === "undefined" || document.readyState === "complete" ) {
+ config.autorun = true;
+}
+
+addEvent(window, "load", function() {
+ // Initialize the config, saving the execution queue
+ var oldconfig = extend({}, config);
+ QUnit.init();
+ extend(config, oldconfig);
+
+ config.blocking = false;
+
+ var userAgent = id("qunit-userAgent");
+ if ( userAgent ) {
+ userAgent.innerHTML = navigator.userAgent;
+ }
+
+ var toolbar = id("qunit-testrunner-toolbar");
+ if ( toolbar ) {
+ toolbar.style.display = "none";
+
+ var filter = document.createElement("input");
+ filter.type = "checkbox";
+ filter.id = "qunit-filter-pass";
+ filter.disabled = true;
+ addEvent( filter, "click", function() {
+ var li = document.getElementsByTagName("li");
+ for ( var i = 0; i < li.length; i++ ) {
+ if ( li[i].className.indexOf("pass") > -1 ) {
+ li[i].style.display = filter.checked ? "none" : "";
+ }
+ }
+ });
+ toolbar.appendChild( filter );
+
+ var label = document.createElement("label");
+ label.setAttribute("for", "qunit-filter-pass");
+ label.innerHTML = "Hide passed tests";
+ toolbar.appendChild( label );
+
+ var missing = document.createElement("input");
+ missing.type = "checkbox";
+ missing.id = "qunit-filter-missing";
+ missing.disabled = true;
+ addEvent( missing, "click", function() {
+ var li = document.getElementsByTagName("li");
+ for ( var i = 0; i < li.length; i++ ) {
+ if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
+ li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
+ }
+ }
+ });
+ toolbar.appendChild( missing );
+
+ label = document.createElement("label");
+ label.setAttribute("for", "qunit-filter-missing");
+ label.innerHTML = "Hide missing tests (untested code is broken code)";
+ toolbar.appendChild( label );
+ }
+
+ var main = id('main');
+ if ( main ) {
+ config.fixture = main.innerHTML;
+ }
+
+ if ( window.jQuery ) {
+ config.ajaxSettings = window.jQuery.ajaxSettings;
+ }
+
+ QUnit.start();
+});
+
+function done() {
+ if ( config.doneTimer && window.clearTimeout ) {
+ window.clearTimeout( config.doneTimer );
+ config.doneTimer = null;
+ }
+
+ if ( config.queue.length ) {
+ config.doneTimer = window.setTimeout(function(){
+ if ( !config.queue.length ) {
+ done();
+ } else {
+ synchronize( done );
+ }
+ }, 13);
+
+ return;
+ }
+
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.currentModule ) {
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+ }
+
+ var banner = id("qunit-banner"),
+ tests = id("qunit-tests"),
+ html = ['Tests completed in ',
+ +new Date - config.started, ' milliseconds.<br/>',
+ '<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
+
+ if ( banner ) {
+ banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
+ }
+
+ if ( tests ) {
+ var result = id("qunit-testresult");
+
+ if ( !result ) {
+ result = document.createElement("p");
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests.nextSibling );
+ }
+
+ result.innerHTML = html;
+ }
+
+ QUnit.done( config.stats.bad, config.stats.all );
+}
+
+function validTest( name ) {
+ var i = config.filters.length,
+ run = false;
+
+ if ( !i ) {
+ return true;
+ }
+
+ while ( i-- ) {
+ var filter = config.filters[i],
+ not = filter.charAt(0) == '!';
+
+ if ( not ) {
+ filter = filter.slice(1);
+ }
+
+ if ( name.indexOf(filter) !== -1 ) {
+ return !not;
+ }
+
+ if ( not ) {
+ run = true;
+ }
+ }
+
+ return run;
+}
+
+function push(result, actual, expected, message) {
+ message = message || (result ? "okay" : "failed");
+ QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
+}
+
+function synchronize( callback ) {
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process();
+ }
+}
+
+function process() {
+ while ( config.queue.length && !config.blocking ) {
+ config.queue.shift()();
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ config.pollution.push( key );
+ }
+ }
+}
+
+function checkPollution( name ) {
+ var old = config.pollution;
+ saveGlobal();
+
+ var newGlobals = diff( old, config.pollution );
+ if ( newGlobals.length > 0 ) {
+ ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
+ config.expected++;
+ }
+
+ var deletedGlobals = diff( config.pollution, old );
+ if ( deletedGlobals.length > 0 ) {
+ ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
+ config.expected++;
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var result = a.slice();
+ for ( var i = 0; i < result.length; i++ ) {
+ for ( var j = 0; j < b.length; j++ ) {
+ if ( result[i] === b[j] ) {
+ result.splice(i, 1);
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function fail(message, exception, callback) {
+ if ( typeof console !== "undefined" && console.error && console.warn ) {
+ console.error(message);
+ console.error(exception);
+ console.warn(callback.toString());
+
+ } else if ( window.opera && opera.postError ) {
+ opera.postError(message, exception, callback.toString);
+ }
+}
+
+function extend(a, b) {
+ for ( var prop in b ) {
+ a[prop] = b[prop];
+ }
+
+ return a;
+}
+
+function addEvent(elem, type, fn) {
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, fn );
+ } else {
+ fn();
+ }
+}
+
+function id(name) {
+ return !!(typeof document !== "undefined" && document && document.getElementById) &&
+ document.getElementById( name );
+}
+
+// Test for equality any JavaScript type.
+// Discussions and reference: http://philrathe.com/articles/equiv
+// Test suites: http://philrathe.com/tests/equiv
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = function () {
+
+ var innerEquiv; // the real equiv function
+ var callers = []; // stack to decide between skip/abort functions
+
+
+ // Determine what is o.
+ function hoozit(o) {
+ if (QUnit.is("String", o)) {
+ return "string";
+
+ } else if (QUnit.is("Boolean", o)) {
+ return "boolean";
+
+ } else if (QUnit.is("Number", o)) {
+
+ if (isNaN(o)) {
+ return "nan";
+ } else {
+ return "number";
+ }
+
+ } else if (typeof o === "undefined") {
+ return "undefined";
+
+ // consider: typeof null === object
+ } else if (o === null) {
+ return "null";
+
+ // consider: typeof [] === object
+ } else if (QUnit.is( "Array", o)) {
+ return "array";
+
+ // consider: typeof new Date() === object
+ } else if (QUnit.is( "Date", o)) {
+ return "date";
+
+ // consider: /./ instanceof Object;
+ // /./ instanceof RegExp;
+ // typeof /./ === "function"; // => false in IE and Opera,
+ // true in FF and Safari
+ } else if (QUnit.is( "RegExp", o)) {
+ return "regexp";
+
+ } else if (typeof o === "object") {
+ return "object";
+
+ } else if (QUnit.is( "Function", o)) {
+ return "function";
+ } else {
+ return undefined;
+ }
+ }
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks(o, callbacks, args) {
+ var prop = hoozit(o);
+ if (prop) {
+ if (hoozit(callbacks[prop]) === "function") {
+ return callbacks[prop].apply(callbacks, args);
+ } else {
+ return callbacks[prop]; // or undefined
+ }
+ }
+ }
+
+ var callbacks = function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality(b, a) {
+ if (b instanceof a.constructor || a instanceof b.constructor) {
+ // to catch short annotaion VS 'new' annotation of a declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function (b) {
+ return isNaN(b);
+ },
+
+ "date": function (b, a) {
+ return hoozit(b) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function (b, a) {
+ return hoozit(b) === "regexp" &&
+ a.source === b.source && // the regex itself
+ a.global === b.global && // and its modifers (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function () {
+ var caller = callers[callers.length - 1];
+ return caller !== Object &&
+ typeof caller !== "undefined";
+ },
+
+ "array": function (b, a) {
+ var i;
+ var len;
+
+ // b could be an object literal here
+ if ( ! (hoozit(b) === "array")) {
+ return false;
+ }
+
+ len = a.length;
+ if (len !== b.length) { // safe and faster
+ return false;
+ }
+ for (i = 0; i < len; i++) {
+ if ( ! innerEquiv(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "object": function (b, a) {
+ var i;
+ var eq = true; // unless we can proove it
+ var aProperties = [], bProperties = []; // collection of strings
+
+ // comparing constructors is more strict than using instanceof
+ if ( a.constructor !== b.constructor) {
+ return false;
+ }
+
+ // stack constructor before traversing properties
+ callers.push(a.constructor);
+
+ for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
+
+ aProperties.push(i); // collect a's properties
+
+ if ( ! innerEquiv(a[i], b[i])) {
+ eq = false;
+ break;
+ }
+ }
+
+ callers.pop(); // unstack, we are done
+
+ for (i in b) {
+ bProperties.push(i); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv(aProperties.sort(), bProperties.sort());
+ }
+ };
+ }();
+
+ innerEquiv = function () { // can take multiple arguments
+ var args = Array.prototype.slice.apply(arguments);
+ if (args.length < 2) {
+ return true; // end transition
+ }
+
+ return (function (a, b) {
+ if (a === b) {
+ return true; // catch the most you can
+ } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [b, a]);
+ }
+
+ // apply transition with (1..n) arguments
+ })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
+ };
+
+ return innerEquiv;
+
+}();
+
+/**
+ * jsDump
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
+ * Date: 5/15/2008
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+ function quote( str ) {
+ return '"' + str.toString().replace(/"/g, '\\"') + '"';
+ };
+ function literal( o ) {
+ return o + '';
+ };
+ function join( pre, arr, post ) {
+ var s = jsDump.separator(),
+ base = jsDump.indent(),
+ inner = jsDump.indent(1);
+ if ( arr.join )
+ arr = arr.join( ',' + s + inner );
+ if ( !arr )
+ return pre + post;
+ return [ pre, inner + arr, base + post ].join(s);
+ };
+ function array( arr ) {
+ var i = arr.length, ret = Array(i);
+ this.up();
+ while ( i-- )
+ ret[i] = this.parse( arr[i] );
+ this.down();
+ return join( '[', ret, ']' );
+ };
+
+ var reName = /^function (\w+)/;
+
+ var jsDump = {
+ parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
+ var parser = this.parsers[ type || this.typeOf(obj) ];
+ type = typeof parser;
+
+ return type == 'function' ? parser.call( this, obj ) :
+ type == 'string' ? parser :
+ this.parsers.error;
+ },
+ typeOf:function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if (typeof obj === "undefined") {
+ type = "undefined";
+ } else if (QUnit.is("RegExp", obj)) {
+ type = "regexp";
+ } else if (QUnit.is("Date", obj)) {
+ type = "date";
+ } else if (QUnit.is("Function", obj)) {
+ type = "function";
+ } else if (QUnit.is("Array", obj)) {
+ type = "array";
+ } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) {
+ type = "window";
+ } else if (QUnit.is("HTMLDocument", obj)) {
+ type = "document";
+ } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) {
+ type = "nodelist";
+ } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) {
+ type = "node";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator:function() {
+ return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
+ },
+ indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
+ if ( !this.multiline )
+ return '';
+ var chr = this.indentChar;
+ if ( this.HTML )
+ chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
+ return Array( this._depth_ + (extra||0) ).join(chr);
+ },
+ up:function( a ) {
+ this._depth_ += a || 1;
+ },
+ down:function( a ) {
+ this._depth_ -= a || 1;
+ },
+ setParser:function( name, parser ) {
+ this.parsers[name] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote:quote,
+ literal:literal,
+ join:join,
+ //
+ _depth_: 1,
+ // This is the list of parsers, to modify them, use jsDump.setParser
+ parsers:{
+ window: '[Window]',
+ document: '[Document]',
+ error:'[ERROR]', //when no parser is found, shouldn't happen
+ unknown: '[Unknown]',
+ 'null':'null',
+ undefined:'undefined',
+ 'function':function( fn ) {
+ var ret = 'function',
+ name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
+ if ( name )
+ ret += ' ' + name;
+ ret += '(';
+
+ ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
+ return join( ret, this.parse(fn,'functionCode'), '}' );
+ },
+ array: array,
+ nodelist: array,
+ arguments: array,
+ object:function( map ) {
+ var ret = [ ];
+ this.up();
+ for ( var key in map )
+ ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
+ this.down();
+ return join( '{', ret, '}' );
+ },
+ node:function( node ) {
+ var open = this.HTML ? '&lt;' : '<',
+ close = this.HTML ? '&gt;' : '>';
+
+ var tag = node.nodeName.toLowerCase(),
+ ret = open + tag;
+
+ for ( var a in this.DOMAttrs ) {
+ var val = node[this.DOMAttrs[a]];
+ if ( val )
+ ret += ' ' + a + '=' + this.parse( val, 'attribute' );
+ }
+ return ret + close + open + '/' + tag + close;
+ },
+ functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
+ var l = fn.length;
+ if ( !l ) return '';
+
+ var args = Array(l);
+ while ( l-- )
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
+ return ' ' + args.join(', ') + ' ';
+ },
+ key:quote, //object calls it internally, the key part of an item in a map
+ functionCode:'[code]', //function calls it internally, it's the content of the function
+ attribute:quote, //node calls it internally, it's an html attribute value
+ string:quote,
+ date:quote,
+ regexp:literal, //regex
+ number:literal,
+ 'boolean':literal
+ },
+ DOMAttrs:{//attributes to dump from nodes, name=>realName
+ id:'id',
+ name:'name',
+ 'class':'className'
+ },
+ HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )
+ indentChar:' ',//indentation unit
+ multiline:true //if true, items in a collection, are separated by a \n, else just a space.
+ };
+
+ return jsDump;
+})();
+
+})(this);
View
59 tests/TestSite/QUnitExt.js
@@ -0,0 +1,59 @@
+(function() {
+ var currentModule = null;
+ var logData = '';
+
+ function appendLog(logText) {
+ logData = logData + logText + '\r\n';
+
+ var logHolder = document.getElementById('qunit-log');
+ if (logHolder) {
+ logHolder.value = logData;
+ }
+ }
+
+ QUnit.log = function(result, message) {
+ if (arguments.length === 1) {
+ message = arguments[0];
+ }
+ appendLog(' ' + message);
+ }
+
+ QUnit.done = function(failures, total) {
+ appendLog('\r\nCompleted; ' + 'failures = ' + failures + '; total = ' + total);
+
+ var logUrl = '/log/' + ((failures === 0) ? 'success' : 'failure');
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ window.open('', '_self', '');
+ window.close();
+ }
+ };
+ xhr.open('POST', logUrl, /* async */ false);
+ xhr.setRequestHeader('Content-Type', 'text/plain');
+ xhr.send(logData);
+ }
+
+ QUnit.testStart = function(name) {
+ appendLog(' Test Started: ' + name);
+ }
+
+ QUnit.testDone = function(name, failures, total) {
+ appendLog(' Test Done: ' + name + '; failures = ' + failures + '; total = ' + total);
+ appendLog('');
+ }
+
+ QUnit.moduleStart = function(name, testEnv) {
+ currentModule = name;
+ appendLog('Module Started: ' + name);
+ }
+
+ QUnit.moduleDone = function(name, failures, total) {
+ if (name === currentModule) {
+ appendLog('Module Done: ' + name + '; failures = ' + failures + '; total = ' + total + '\r\n');
+ appendLog('');
+ appendLog('');
+ }
+ currentModule = null;
+ }
+})();
View
117 tests/TestSite/TestOOP.htm
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>TypeSystemTests - OOP</title>
+ <link rel="stylesheet" href="QUnit.css" type="text/css" />
+ <script type="text/javascript" src="QUnit.js"></script>
+ <script type="text/javascript" src="QUnitExt.js"></script>
+</head>
+<body>
+ <h1 id="qunit-header">Test Results</h1>
+ <h2 id="qunit-banner"></h2>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"></ol>
+ <br />
+ <textarea id="qunit-log" rows="10" cols="100"></textarea>
+</body>
+
+<script type="text/javascript" src="Scripts/ssloader.debug.js"></script>
+<script type="text/script" data-name="ss" data-src="Scripts/ss.debug.js"></script>
+<script type="text/script" data-name="oop" data-src="Scripts/OOP.js"></script>
+
+<script type="text/script">
+define(['ss', 'oop'], function(ss, oop) {
+ module('OOP');
+ test('isClass', function() {
+ QUnit.equal(ss.isClass(oop.Cat), true, 'Expected Cat to be a class');
+ QUnit.equal(ss.isClass(oop.IPet), false, 'Did not expect IPet to be a class');
+ QUnit.equal(ss.isClass(oop.Comic), true, 'Expectd Comic to be a class');
+ });
+
+ test('isInteface', function() {
+ QUnit.equal(ss.isInterface(oop.Cat), false, 'Did not expect Cat to be an interface');
+ QUnit.equal(ss.isInterface(oop.IPet), true, 'Expected IPet to be an interface');
+ QUnit.equal(ss.isInterface(oop.Comic), false, 'Did not expect Comic to be an interface');
+ });
+
+ test('canAssign', function() {
+ QUnit.equal(ss.canAssign(oop.Animal, oop.Cat), true, 'Cat should be assignable to an Animal');
+ QUnit.equal(ss.canAssign(oop.IMammal, oop.Cat), true, 'Cat should be assignable to an IMammal');
+ QUnit.equal(ss.canAssign(oop.IMammal, oop.Garfield), true, 'Garfield should be assignable to an IMammal');
+ QUnit.equal(ss.canAssign(oop.IPet, oop.Cat), false, 'Cat should not be assignable to an IPet');
+ QUnit.equal(ss.canAssign(Object, oop.Cat), true, 'Cat should be assignable to an Object');
+ });
+
+ test('isOfType', function() {
+ var c = new oop.Cat();
+ var g = new oop.Garfield();
+
+ QUnit.equal(ss.isOfType(c, oop.Animal), true, 'Cat instance should be an Animal instance');
+ QUnit.equal(ss.isOfType(c, oop.IMammal), true, 'Cat instance should be an IMammal instance');
+ QUnit.equal(ss.isOfType(g, oop.IMammal), true, 'Garfield instance should be an IMammal instance');
+ QUnit.equal(ss.isOfType(c, oop.Zoo), false, 'Cat instance should not be a Zoo instance');
+ QUnit.equal(ss.isOfType(c, oop.IPet), false, 'Cat instance should not be an IPet instance');
+ QUnit.equal(ss.isOfType(c, Object), true, 'Cat instance should not be an Object instance');
+ });
+
+ test('baseType', function() {
+ QUnit.equal(ss.typeName(ss.baseType(oop.Cat)), 'Animal', 'Cat\'s base type should be Animal');
+ QUnit.equal(ss.baseType(oop.Animal), Object, 'Animal\'s base type should be Object');
+ QUnit.equal(ss.baseType(Object), Object, 'Object\'s base type should be Object');
+ });
+
+ test('canCast and safeCast', function() {
+ var g = new oop.Garfield();
+
+ QUnit.equal(ss.canCast(g, oop.IPet), true, 'Garfield instance should be castable to Test.IPet');
+ QUnit.equal(ss.canCast(g, oop.Animal), true, 'Garfield instance should be castable to Test.Animal');
+ QUnit.equal(ss.canCast(g, oop.IObject), false, 'Garfield instance should not be castable to IObject');
+ QUnit.equal(ss.safeCast(g, oop.IPet), g, 'Garfield instance should be castable to Test.IPet');
+ QUnit.equal(ss.safeCast(g, oop.Animal), g, 'Garfield instance should be castable to Test.Animal');
+ QUnit.equal(ss.safeCast(g, oop.IObject), null, 'Garfield instance should not be castable to IObject');
+ });
+
+ test('base ctor', function() {
+ var c = new oop.Cat();
+ QUnit.equal(c.get_species(), 'Cat', 'Cat is of cat species');
+ });
+
+ test('base method', function() {
+ var g = new oop.Garfield();
+ QUnit.equal(g.speak(), 'meow\r\nTranslation: I am fat, lazy, and cynical, but still, a favorite cat...', 'Garfield meows and says something cute');
+ });
+
+ test('getType', function() {
+ var g = new oop.Garfield();
+ QUnit.equal(ss.getType(g), oop.Garfield, "Expected Garfield instance.");
+
+ var c = new oop.Comic();
+ QUnit.equal(ss.getType(c), oop.Comic, "Expected Comic instance.");
+ });
+
+ test('typeNames', function() {
+ var g = new oop.Garfield();
+ QUnit.equal(ss.typeName(ss.getType(g)), 'Garfield', "Expected 'Garfield' for type name.");
+
+ var c = new oop.Comic();
+ QUnit.equal(ss.typeName(ss.getType(c)), 'Comic', "Expected 'Comic' for type name.");
+ });
+
+ test('type parsing', function() {
+ var t1 = ss.type('String');
+ var t2 = ss.type('oop.Zoo');
+ var t3 = ss.type('oop.Garfield');
+ var t4 = ss.type('oop.Comic');
+ var t5 = ss.type('oop.Foo');
+ var t6 = ss.type('xyz.Bar');
+
+ QUnit.equal(t1, String, "Expected String type.");
+ QUnit.equal(t2, oop.Zoo, "Expected Zoo type.");
+ QUnit.equal(t3, oop.Garfield, "Expected Garfield type.");
+ QUnit.equal(t4, oop.Comic, "Expected Comic type.");
+ QUnit.equal(t5, null, "Expected null type.");
+ QUnit.equal(t6, null, "Expected null type.");
+ });
+});
+</script>
+</html>
View
0  tests/ScriptSharp.testsettings → tests/Tests.testsettings
File renamed without changes
Please sign in to comment.
Something went wrong with that request. Please try again.