More robust system for testing compilation errors
This commit is contained in:
10
src/kiss/EType.hx
Normal file
10
src/kiss/EType.hx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package kiss;
|
||||||
|
|
||||||
|
enum EType {
|
||||||
|
EStream(message:String);
|
||||||
|
EKiss(message:String);
|
||||||
|
EUnmatchedBracket(type:String);
|
||||||
|
EException(message:String);
|
||||||
|
EExpected(e:EType);
|
||||||
|
EUnexpected(e:Dynamic);
|
||||||
|
}
|
145
src/kiss/Kiss.hx
145
src/kiss/Kiss.hx
@@ -17,7 +17,9 @@ import kiss.Macros;
|
|||||||
import kiss.KissError;
|
import kiss.KissError;
|
||||||
import kiss.cloner.Cloner;
|
import kiss.cloner.Cloner;
|
||||||
import tink.syntaxhub.*;
|
import tink.syntaxhub.*;
|
||||||
|
import tink.macro.Exprs;
|
||||||
import haxe.ds.Either;
|
import haxe.ds.Either;
|
||||||
|
import kiss.EType;
|
||||||
|
|
||||||
using kiss.Kiss;
|
using kiss.Kiss;
|
||||||
using kiss.Helpers;
|
using kiss.Helpers;
|
||||||
@@ -218,29 +220,73 @@ class Kiss {
|
|||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function _try<T>(operation:() -> T):Null<T> {
|
public static function _try<T>(operation:() -> T, ?expectedError:EType):Null<T> {
|
||||||
#if !macrotest
|
#if !macrotest
|
||||||
try {
|
try {
|
||||||
#end
|
#end
|
||||||
return operation();
|
return operation();
|
||||||
#if !macrotest
|
#if !macrotest
|
||||||
} catch (err:StreamError) {
|
} catch (err:StreamError) {
|
||||||
Sys.stderr().writeString(err + "\n");
|
function printErr() {
|
||||||
Sys.exit(1);
|
Sys.stderr().writeString(err + "\n");
|
||||||
return null;
|
}
|
||||||
|
switch (expectedError) {
|
||||||
|
case EStream(message) if (message == err.message):
|
||||||
|
throw EExpected(expectedError);
|
||||||
|
case null:
|
||||||
|
printErr();
|
||||||
|
Sys.exit(1);
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
printErr();
|
||||||
|
throw EUnexpected(err);
|
||||||
|
}
|
||||||
} catch (err:KissError) {
|
} catch (err:KissError) {
|
||||||
Sys.stderr().writeString(err + "\n");
|
function printErr() {
|
||||||
Sys.exit(1);
|
Sys.stderr().writeString(err + "\n");
|
||||||
return null;
|
}
|
||||||
|
switch (expectedError) {
|
||||||
|
case EKiss(message) if (message == err.message):
|
||||||
|
throw EExpected(expectedError);
|
||||||
|
case null:
|
||||||
|
printErr();
|
||||||
|
Sys.exit(1);
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
printErr();
|
||||||
|
throw EUnexpected(err);
|
||||||
|
}
|
||||||
} catch (err:UnmatchedBracketSignal) {
|
} catch (err:UnmatchedBracketSignal) {
|
||||||
Sys.stderr().writeString(Stream.toPrint(err.position) + ': Unmatched ${err.type}\n');
|
function printErr() {
|
||||||
Sys.exit(1);
|
Sys.stderr().writeString(Stream.toPrint(err.position) + ': Unmatched ${err.type}\n');
|
||||||
return null;
|
}
|
||||||
|
switch (expectedError) {
|
||||||
|
case EUnmatchedBracket(type) if (type == err.type):
|
||||||
|
throw EExpected(expectedError);
|
||||||
|
case null:
|
||||||
|
printErr();
|
||||||
|
Sys.exit(1);
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
printErr();
|
||||||
|
throw EUnexpected(err);
|
||||||
|
}
|
||||||
} catch (err:Exception) {
|
} catch (err:Exception) {
|
||||||
Sys.stderr().writeString("Error: " + err.message + "\n");
|
function printErr() {
|
||||||
Sys.stderr().writeString(err.stack.toString() + "\n");
|
Sys.stderr().writeString("Error: " + err.message + "\n");
|
||||||
Sys.exit(1);
|
Sys.stderr().writeString(err.stack.toString() + "\n");
|
||||||
return null;
|
}
|
||||||
|
switch (expectedError) {
|
||||||
|
case EException(message) if (message == err.message):
|
||||||
|
throw EExpected(expectedError);
|
||||||
|
case null:
|
||||||
|
printErr();
|
||||||
|
Sys.exit(1);
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
printErr();
|
||||||
|
throw EUnexpected(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
@@ -297,10 +343,77 @@ class Kiss {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is only for testing:
|
||||||
|
public static function buildExpectingError(expectedError:ExprOf<EType>, ?kissFile:String, ?k:KissState, useClassFields = true, ?context:FrontendContext):Array<Field> {
|
||||||
|
var buildFields = Context.getBuildFields();
|
||||||
|
var hasTestExpectedError = false;
|
||||||
|
for (field in buildFields) {
|
||||||
|
switch (field) {
|
||||||
|
case {
|
||||||
|
name: "testExpectedError",
|
||||||
|
kind: FFun({
|
||||||
|
params: [],
|
||||||
|
expr: {
|
||||||
|
expr: EBlock([{expr: ECall({expr: EConst(CIdent("_testExpectedError"))}, [])}])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}:
|
||||||
|
hasTestExpectedError = true;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasTestExpectedError) {
|
||||||
|
throw "When building with Kiss.buildExpectingError(), you must add this Haxe function: " +
|
||||||
|
"function testExpectedError() { _testExpectedError(); }";
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedError = Exprs.eval(expectedError);
|
||||||
|
var s = Std.string(expectedError);
|
||||||
|
|
||||||
|
try {
|
||||||
|
build(kissFile, k, useClassFields, context, expectedError);
|
||||||
|
|
||||||
|
// Build success, which is bad:
|
||||||
|
buildFields.push({
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
name: "_testExpectedError",
|
||||||
|
kind: FFun({
|
||||||
|
args: [],
|
||||||
|
expr: macro utest.Assert.fail('Build succeeded when an error was expected: ' + $v{s})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} catch (e:EType) {
|
||||||
|
switch (e) {
|
||||||
|
case EExpected(e):
|
||||||
|
buildFields.push({
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
name: "_testExpectedError",
|
||||||
|
kind: FFun({
|
||||||
|
args: [],
|
||||||
|
expr: macro utest.Assert.pass()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
case EUnexpected(e):
|
||||||
|
buildFields.push({
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
name: "_testExpectedError",
|
||||||
|
kind: FFun({
|
||||||
|
args: [],
|
||||||
|
expr: macro utest.Assert.fail('Build failed in an unexpected way. Expected: ' + $v{s})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
throw "unexpected error is neither expected nor unexpected ¯\\_(ツ)_/¯";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildFields;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Build macro: add fields to a class from a corresponding .kiss file
|
Build macro: add fields to a class from a corresponding .kiss file
|
||||||
**/
|
**/
|
||||||
public static function build(?kissFile:String, ?k:KissState, useClassFields = true, ?context:FrontendContext):Array<Field> {
|
public static function build(?kissFile:String, ?k:KissState, useClassFields = true, ?context:FrontendContext, ?expectedError:EType):Array<Field> {
|
||||||
|
|
||||||
var classPath = Context.getPosInfos(Context.currentPos()).file;
|
var classPath = Context.getPosInfos(Context.currentPos()).file;
|
||||||
// (load... ) relative to the original file
|
// (load... ) relative to the original file
|
||||||
@@ -364,7 +477,7 @@ class Kiss {
|
|||||||
|
|
||||||
#end
|
#end
|
||||||
k.fieldList;
|
k.fieldList;
|
||||||
});
|
}, expectedError);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ enum ErrorType {
|
|||||||
// Internal Kiss errors
|
// Internal Kiss errors
|
||||||
class KissError {
|
class KissError {
|
||||||
var exps:List<ReaderExp>;
|
var exps:List<ReaderExp>;
|
||||||
var message:String;
|
public var message(default, null):String;
|
||||||
|
|
||||||
public function new(exps:Array<ReaderExp>, message:String) {
|
public function new(exps:Array<ReaderExp>, message:String) {
|
||||||
this.exps = exps;
|
this.exps = exps;
|
||||||
|
@@ -18,7 +18,7 @@ typedef Position = {
|
|||||||
|
|
||||||
class StreamError {
|
class StreamError {
|
||||||
var position:Position;
|
var position:Position;
|
||||||
var message:String;
|
public var message(default,null):String;
|
||||||
|
|
||||||
public function new(position:Position, message:String) {
|
public function new(position:Position, message:String) {
|
||||||
this.position = position;
|
this.position = position;
|
||||||
@@ -54,7 +54,7 @@ class Stream {
|
|||||||
var file = "string";
|
var file = "string";
|
||||||
if (position != null) {
|
if (position != null) {
|
||||||
file = position.file;
|
file = position.file;
|
||||||
}
|
}
|
||||||
var s = new Stream(file, content);
|
var s = new Stream(file, content);
|
||||||
if (position != null) {
|
if (position != null) {
|
||||||
s.line = position.line;
|
s.line = position.line;
|
||||||
@@ -140,7 +140,7 @@ class Stream {
|
|||||||
startOfLine = false;
|
startOfLine = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function record() {
|
function record() {
|
||||||
recording += content.substr(0, count);
|
recording += content.substr(0, count);
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ class Stream {
|
|||||||
record();
|
record();
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
content = content.substr(count);
|
content = content.substr(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ class Stream {
|
|||||||
|
|
||||||
private var recordingType:StreamRecordType = Neither;
|
private var recordingType:StreamRecordType = Neither;
|
||||||
private var recording = "";
|
private var recording = "";
|
||||||
|
|
||||||
public function recordTransaction(type:StreamRecordType = Both, transaction:Void->Void) {
|
public function recordTransaction(type:StreamRecordType = Both, transaction:Void->Void) {
|
||||||
if (type == Neither) {
|
if (type == Neither) {
|
||||||
error(this, "Tried to start recording a transaction that would always return an empty string");
|
error(this, "Tried to start recording a transaction that would always return an empty string");
|
||||||
|
12
src/test/cases/UnmatchedBracketTestCase.hx
Normal file
12
src/test/cases/UnmatchedBracketTestCase.hx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package test.cases;
|
||||||
|
|
||||||
|
import utest.Test;
|
||||||
|
import utest.Assert;
|
||||||
|
import kiss.Prelude;
|
||||||
|
|
||||||
|
@:build(kiss.Kiss.buildExpectingError(kiss.EType.EUnmatchedBracket("}")))
|
||||||
|
class UnmatchedBracketTestCase extends Test {
|
||||||
|
function testExpectedError() {
|
||||||
|
_testExpectedError();
|
||||||
|
}
|
||||||
|
}
|
1
src/test/cases/UnmatchedBracketTestCase.kiss
Normal file
1
src/test/cases/UnmatchedBracketTestCase.kiss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
}
|
Reference in New Issue
Block a user