|
@@ -25,10 +25,10 @@ package unit;
|
|
import haxe.macro.Context;
|
|
import haxe.macro.Context;
|
|
import haxe.macro.Expr;
|
|
import haxe.macro.Expr;
|
|
import haxe.macro.Type;
|
|
import haxe.macro.Type;
|
|
|
|
+
|
|
using StringTools;
|
|
using StringTools;
|
|
|
|
|
|
class UnitBuilder {
|
|
class UnitBuilder {
|
|
-
|
|
|
|
static public macro function generateSpec(basePath:String) {
|
|
static public macro function generateSpec(basePath:String) {
|
|
var ret = [];
|
|
var ret = [];
|
|
var numFiles = 0;
|
|
var numFiles = 0;
|
|
@@ -46,34 +46,43 @@ class UnitBuilder {
|
|
params: [],
|
|
params: [],
|
|
expr: read(filePath)
|
|
expr: read(filePath)
|
|
}
|
|
}
|
|
- var p = Context.makePosition( { min:0, max:0, file:filePath + file } );
|
|
|
|
|
|
+ var p = Context.makePosition({min: 0, max: 0, file: filePath + file});
|
|
var field = {
|
|
var field = {
|
|
name: "test",
|
|
name: "test",
|
|
kind: FFun(func),
|
|
kind: FFun(func),
|
|
pos: p,
|
|
pos: p,
|
|
access: [APublic],
|
|
access: [APublic],
|
|
doc: null,
|
|
doc: null,
|
|
- meta: []
|
|
|
|
|
|
+ meta: file.endsWith("Utf8.unit.hx") ? [{name: ":haxe.warning", params: [macro "-WDeprecated"], pos: p}] : []
|
|
};
|
|
};
|
|
var pack = ["unit", "spec"].concat(pack);
|
|
var pack = ["unit", "spec"].concat(pack);
|
|
var typeName = "Test" + file.substr(0, file.indexOf("."));
|
|
var typeName = "Test" + file.substr(0, file.indexOf("."));
|
|
- Context.defineModule(pack.join(".") + "." + typeName, [{
|
|
|
|
- pack: pack,
|
|
|
|
- name: typeName,
|
|
|
|
- pos: p,
|
|
|
|
- kind: TDClass({
|
|
|
|
- pack: ["unit"],
|
|
|
|
- name: "Test"
|
|
|
|
- }),
|
|
|
|
- fields: [field]
|
|
|
|
- }], [{
|
|
|
|
- path: [{pos: p, name: "unit"}, {pos: p, name: "spec"}, {pos: p, name: "TestSpecification"}],
|
|
|
|
- mode: INormal
|
|
|
|
- }, {
|
|
|
|
- // TODO: import.hx doesn't work for this?
|
|
|
|
- path: [{pos: p, name: "haxe"}, {pos: p, name: "macro"}, {pos: p, name: "Expr"}],
|
|
|
|
- mode: INormal
|
|
|
|
- }]);
|
|
|
|
|
|
+ Context.defineModule(pack.join(".") + "." + typeName, [
|
|
|
|
+ {
|
|
|
|
+ pack: pack,
|
|
|
|
+ name: typeName,
|
|
|
|
+ pos: p,
|
|
|
|
+ kind: TDClass({
|
|
|
|
+ pack: ["unit"],
|
|
|
|
+ name: "Test"
|
|
|
|
+ }),
|
|
|
|
+ fields: [field]
|
|
|
|
+ }
|
|
|
|
+ ], [
|
|
|
|
+ {
|
|
|
|
+ path: [
|
|
|
|
+ {pos: p, name: "unit"},
|
|
|
|
+ {pos: p, name: "spec"},
|
|
|
|
+ {pos: p, name: "TestSpecification"}
|
|
|
|
+ ],
|
|
|
|
+ mode: INormal
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ // TODO: import.hx doesn't work for this?
|
|
|
|
+ path: [{pos: p, name: "haxe"}, {pos: p, name: "macro"}, {pos: p, name: "Expr"}],
|
|
|
|
+ mode: INormal
|
|
|
|
+ }
|
|
|
|
+ ]);
|
|
var tp:TypePath = {
|
|
var tp:TypePath = {
|
|
pack: pack,
|
|
pack: pack,
|
|
name: typeName
|
|
name: typeName
|
|
@@ -81,7 +90,7 @@ class UnitBuilder {
|
|
ret.push(macro new $tp());
|
|
ret.push(macro new $tp());
|
|
} else if (sys.FileSystem.isDirectory(filePath)) {
|
|
} else if (sys.FileSystem.isDirectory(filePath)) {
|
|
readDir(filePath, pack.concat([file]));
|
|
readDir(filePath, pack.concat([file]));
|
|
- } else if(filePath.endsWith('.hx')) {
|
|
|
|
|
|
+ } else if (filePath.endsWith('.hx')) {
|
|
Context.error('$filePath: specification tests filenames should end with ".unit.hx"', Context.currentPos());
|
|
Context.error('$filePath: specification tests filenames should end with ".unit.hx"', Context.currentPos());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -93,28 +102,32 @@ class UnitBuilder {
|
|
|
|
|
|
#if macro
|
|
#if macro
|
|
static function collapseToOrExpr(el:Array<Expr>) {
|
|
static function collapseToOrExpr(el:Array<Expr>) {
|
|
- return switch(el) {
|
|
|
|
|
|
+ return switch (el) {
|
|
case []: throw "";
|
|
case []: throw "";
|
|
case [e]: e;
|
|
case [e]: e;
|
|
- case _:
|
|
|
|
- var e = el.pop();
|
|
|
|
- { expr: EBinop(OpBoolOr, e, collapseToOrExpr(el)), pos: e.pos }
|
|
|
|
|
|
+ case _:
|
|
|
|
+ var e = el.pop();
|
|
|
|
+ {expr: EBinop(OpBoolOr, e, collapseToOrExpr(el)), pos: e.pos}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static function mkEq(e1, e2, p:Position) {
|
|
static function mkEq(e1, e2, p:Position) {
|
|
function isFloat(e) {
|
|
function isFloat(e) {
|
|
- try return switch(Context.follow(Context.typeof(e))) {
|
|
|
|
- case TAbstract(tr, _):
|
|
|
|
- tr.get().name == "Float";
|
|
|
|
- case _:
|
|
|
|
- false;
|
|
|
|
- } catch (e:Dynamic) {
|
|
|
|
|
|
+ try
|
|
|
|
+ return switch (Context.follow(Context.typeof(e))) {
|
|
|
|
+ case TAbstract(tr, _):
|
|
|
|
+ tr.get().name == "Float";
|
|
|
|
+ case _:
|
|
|
|
+ false;
|
|
|
|
+ } catch (e:Dynamic) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var e = switch [isFloat(e1) || isFloat(e2), e2.expr] {
|
|
var e = switch [isFloat(e1) || isFloat(e2), e2.expr] {
|
|
- case [_, EField( { expr:EConst(CIdent("Math" | "math")) }, "POSITIVE_INFINITY" | "NEGATIVE_INFINITY")] if (Context.defined("cpp") || Context.defined("php")):
|
|
|
|
|
|
+ case [
|
|
|
|
+ _,
|
|
|
|
+ EField({expr: EConst(CIdent("Math" | "math"))}, "POSITIVE_INFINITY" | "NEGATIVE_INFINITY")
|
|
|
|
+ ] if (Context.defined("cpp") || Context.defined("php")):
|
|
macro t($e1 == $e2);
|
|
macro t($e1 == $e2);
|
|
case [true, _]:
|
|
case [true, _]:
|
|
macro feq($e1, $e2);
|
|
macro feq($e1, $e2);
|
|
@@ -126,12 +139,13 @@ class UnitBuilder {
|
|
pos: p
|
|
pos: p
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
static public function read(path:String) {
|
|
static public function read(path:String) {
|
|
- var p = Context.makePosition( { min:0, max:0, file:path } );
|
|
|
|
|
|
+ var p = Context.makePosition({min: 0, max: 0, file: path});
|
|
var file = sys.io.File.getContent(path);
|
|
var file = sys.io.File.getContent(path);
|
|
var code = Context.parseInlineString("{" + file + "\n}", p);
|
|
var code = Context.parseInlineString("{" + file + "\n}", p);
|
|
function mkBlock(e:Expr) {
|
|
function mkBlock(e:Expr) {
|
|
- return switch(e.expr) {
|
|
|
|
|
|
+ return switch (e.expr) {
|
|
case EBlock(b): b;
|
|
case EBlock(b): b;
|
|
case _: [e];
|
|
case _: [e];
|
|
}
|
|
}
|
|
@@ -139,30 +153,24 @@ class UnitBuilder {
|
|
function bl(block:Array<Expr>):Array<Expr> {
|
|
function bl(block:Array<Expr>):Array<Expr> {
|
|
var ret = [];
|
|
var ret = [];
|
|
for (e in block) {
|
|
for (e in block) {
|
|
- var e = switch(e.expr) {
|
|
|
|
- case EBinop(OpEq, e1, { expr: EConst(CIdent("false")) } )
|
|
|
|
- | EBinop(OpEq, { expr: EConst(CIdent("false")) }, e1):
|
|
|
|
|
|
+ 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,
|
|
expr: (macro f($e1)).expr,
|
|
pos: e.pos
|
|
pos: e.pos
|
|
}
|
|
}
|
|
- case EBinop(OpEq, e1, { expr: EConst(CIdent("true")) } )
|
|
|
|
- | EBinop(OpEq, { expr: EConst(CIdent("true")) }, e1):
|
|
|
|
|
|
+ case EBinop(OpEq, e1, {expr: EConst(CIdent("true"))}) | EBinop(OpEq, {expr: EConst(CIdent("true"))}, e1):
|
|
{
|
|
{
|
|
expr: (macro t($e1)).expr,
|
|
expr: (macro t($e1)).expr,
|
|
pos: e.pos
|
|
pos: e.pos
|
|
}
|
|
}
|
|
- case EBinop(OpEq, e1, { expr: EArrayDecl(el) } )
|
|
|
|
- | EBinop(OpEq, { expr: EArrayDecl(el) }, e1 ):
|
|
|
|
|
|
+ case EBinop(OpEq, e1, {expr: EArrayDecl(el)}) | EBinop(OpEq, {expr: EArrayDecl(el)}, e1):
|
|
var el2 = [];
|
|
var el2 = [];
|
|
for (i in 0...el.length) {
|
|
for (i in 0...el.length) {
|
|
var e2 = el[i];
|
|
var e2 = el[i];
|
|
el2.push(mkEq((macro $e1[$v{i}]), e2, e.pos));
|
|
el2.push(mkEq((macro $e1[$v{i}]), e2, e.pos));
|
|
}
|
|
}
|
|
- if (el2.length == 0)
|
|
|
|
- mkEq((macro @:pos(e1.pos) $e1.length), (macro 0), e.pos);
|
|
|
|
- else
|
|
|
|
- macro { $a{el2}; };
|
|
|
|
|
|
+ if (el2.length == 0) mkEq((macro @:pos(e1.pos) $e1.length), (macro 0), e.pos); else macro {$a{el2};};
|
|
case EBinop(OpEq, e1, e2):
|
|
case EBinop(OpEq, e1, e2):
|
|
mkEq(e1, e2, e.pos);
|
|
mkEq(e1, e2, e.pos);
|
|
case EBinop(OpNotEq, e1, e2):
|
|
case EBinop(OpNotEq, e1, e2):
|
|
@@ -174,18 +182,18 @@ class UnitBuilder {
|
|
}
|
|
}
|
|
case EThrow(e):
|
|
case EThrow(e):
|
|
macro exc(function() $e);
|
|
macro exc(function() $e);
|
|
- case EBinop(OpIn, e1, {expr:EArrayDecl(el) }):
|
|
|
|
|
|
+ case EBinop(OpIn, e1, {expr: EArrayDecl(el)}):
|
|
var el2 = [];
|
|
var el2 = [];
|
|
for (e in el)
|
|
for (e in el)
|
|
el2.push(macro $e1 == $e);
|
|
el2.push(macro $e1 == $e);
|
|
- macro @:pos(e.pos) t(${ collapseToOrExpr(el2) } );
|
|
|
|
|
|
+ macro @:pos(e.pos) t(${collapseToOrExpr(el2)});
|
|
case EVars(vl):
|
|
case EVars(vl):
|
|
for (v in vl)
|
|
for (v in vl)
|
|
if (v.name == "t" || v.name == "f" || v.name == "eq" || v.name == "neq")
|
|
if (v.name == "t" || v.name == "f" || v.name == "eq" || v.name == "neq")
|
|
Context.error('${v.name} is reserved for unit testing', e.pos);
|
|
Context.error('${v.name} is reserved for unit testing', e.pos);
|
|
- e;
|
|
|
|
|
|
+ e;
|
|
case EFor(it, {expr: EBlock(el), pos: p}):
|
|
case EFor(it, {expr: EBlock(el), pos: p}):
|
|
- { expr: EFor(it, {expr:EBlock(bl(el)), pos: p}), pos: e.pos };
|
|
|
|
|
|
+ {expr: EFor(it, {expr: EBlock(bl(el)), pos: p}), pos: e.pos};
|
|
case _:
|
|
case _:
|
|
e;
|
|
e;
|
|
}
|
|
}
|
|
@@ -197,4 +205,4 @@ class UnitBuilder {
|
|
return macro $b{bl(block)};
|
|
return macro $b{bl(block)};
|
|
}
|
|
}
|
|
#end
|
|
#end
|
|
-}
|
|
|
|
|
|
+}
|