123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- /*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- package unit;
- import haxe.macro.Context;
- import haxe.macro.Expr;
- import haxe.macro.Type;
- using StringTools;
- class UnitBuilder {
-
- static public macro function build(basePath:String):Array<Field> {
- var ret = Context.getBuildFields();
- var numFiles = 0;
-
- function readDir(path) {
- var dir = sys.FileSystem.readDirectory(path);
- path = path.endsWith("\\") || path.endsWith("/") ? path : path + "/";
- for (file in dir) {
- var filePath = path + file;
- if (file.endsWith(".unit.hx")) {
- numFiles++;
- var func = {
- args: [],
- ret: null,
- params: [],
- expr: read(filePath)
- }
- ret.push( {
- name: "test" + ~/\./g.map(file, function(_) return "_"),
- kind: FFun(func),
- pos: Context.makePosition( { min:0, max:0, file:filePath + file } ),
- access: [APublic],
- doc: null,
- meta: []
- });
- } else if (sys.FileSystem.isDirectory(filePath)) {
- readDir(filePath);
- }
- }
- }
- readDir(basePath);
- //trace("Added " +numFiles + " .unit.hx files");
- return ret;
- }
-
- #if macro
- static function collapseToOrExpr(el:Array<Expr>) {
- return switch(el) {
- case []: throw "";
- case [e]: e;
- case _:
- var e = el.pop();
- { expr: EBinop(OpBoolOr, e, collapseToOrExpr(el)), pos: e.pos }
- }
- }
-
- static function mkEq(e1, e2, p) {
- function isFloat(e) {
- try return switch(Context.typeof(e)) {
- case TAbstract(tr, _):
- tr.get().name == "Float";
- case _:
- false;
- } catch (e:Dynamic) {
- return false;
- }
- }
- var e = switch [isFloat(e1) || isFloat(e2), e2.expr] {
- // hell yeah
- case [true, EField( { expr:EConst(CIdent("Math")) }, "POSITIVE_INFINITY" | "NEGATIVE_INFINITY")] if (Context.defined("cpp") || Context.defined("php")):
- macro t($e1 == $e2);
- case [true, _]:
- macro feq($e1, $e2);
- case _:
- macro eq($e1, $e2);
- }
- return {
- expr: e.expr,
- pos: p
- }
- }
- static public function read(path:String) {
- var p = Context.makePosition( { min:0, max:0, file:path } );
- var file = sys.io.File.getContent(path);
- var code = Context.parseInlineString("{" + file + "}", p);
- var block = switch(code.expr) {
- case EBlock(b): b;
- case _: throw "false";
- }
- var ret = [];
- for (e in block) {
- var e = switch(e.expr) {
- case EBinop(OpEq, e1, { expr: EConst(CIdent("false")) } )
- | EBinop(OpEq, { expr: EConst(CIdent("false")) }, e1):
- {
- expr: (macro f($e1)).expr,
- pos: e.pos
- }
- case EBinop(OpEq, e1, { expr: EConst(CIdent("true")) } )
- | EBinop(OpEq, { expr: EConst(CIdent("true")) }, e1):
- {
- expr: (macro t($e1)).expr,
- pos: e.pos
- }
- case EBinop(OpEq, e1, { expr: EArrayDecl(el) } )
- | EBinop(OpEq, { expr: EArrayDecl(el) }, e1 ):
- var el2 = [];
- for (i in 0...el.length) {
- var e2 = el[i];
- el2.push(mkEq((macro $e1[$v{i}]), e2, e.pos));
- }
- if (el2.length == 0)
- mkEq((macro $e1.length), (macro 0), e.pos);
- else
- macro { $a{el2}; };
- case EBinop(OpEq, e1, e2):
- mkEq(e1, e2, e.pos);
- case EBinop(OpGt | OpGte | OpLt | OpLte, _, _):
- {
- expr: (macro t($e)).expr,
- pos: e.pos
- }
- case EThrow(e):
- macro exc(function() $e);
- case EIn(e1, {expr:EArrayDecl(el) }):
- var el2 = [];
- for (e in el)
- el2.push(macro $e1 == $e);
- macro @:pos(e.pos) t(${ collapseToOrExpr(el2) } );
- case EVars(vl):
- for (v in vl)
- if (v.name == "t" || v.name == "f" || v.name == "eq" || v.name == "neq")
- Context.error('${v.name} is reserved for unit testing', e.pos);
- e;
- case _:
- e;
- }
- ret.push(e);
- }
- return macro { $a{ret}; };
- }
- #end
- }
|