Skip to content

Commit

Permalink
Merge pull request #21 from ellemenno/v2.0.0
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
pixeldroid authored Sep 18, 2017
2 parents 449e021 + efaa059 commit bf8d405
Show file tree
Hide file tree
Showing 27 changed files with 1,451 additions and 233 deletions.
101 changes: 74 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ a simple specification framework for [Loom][loom-sdk]
Download the library into its matching sdk folder:

$ curl -L -o ~/.loom/sdks/sprint34/libs/Spec.loomlib \
https://github.com/pixeldroid/spec-ls/releases/download/v1.3.1/Spec-sprint34.loomlib
https://github.com/pixeldroid/spec-ls/releases/download/v2.0.0/Spec-sprint34.loomlib

To uninstall, simply delete the file:

Expand All @@ -25,9 +25,17 @@ To uninstall, simply delete the file:

## usage

0. import Spec, a Reporter, and your specifications
0. have the specifications describe themselves to Spec
0. add your reporter(s) to Spec and execute
### in a nutshell

0. import `Spec`, one or more `Reporter`s, and one or more specifications
0. add the reporter(s) to an instance of `Spec`
0. in the specifications, describe the desired behavior of the thing they validate
* `Spec.describe()` instantiates a `Thing`
* `Thing.should()` declares a requirement function
- in the function, `expects()` and `asserts()` validate the requirement
0. execute the spec to see results from the reporter

### simple example

```ls
package
Expand All @@ -37,31 +45,49 @@ package
import pixeldroid.bdd.Spec;
import pixeldroid.bdd.reporters.ConsoleReporter;
import WidgetSpec;
public class SpecTest extends Application
{
override public function run():void
{
MySpec.describe();
var spec:Spec = new Spec();
spec.addReporter(new ConsoleReporter());
Spec.addReporter(new ConsoleReporter());
Spec.execute();
WidgetSpec.specify(spec);
spec.execute();
}
}
import pixeldroid.bdd.Spec;
import pixeldroid.bdd.Thing;
public static class MySpec
public static class WidgetSpec
{
public static function describe():void
private static var it:Thing;
public static function specify(specifier:Spec):void
{
it = specifier.describe('Widget');
it.should('be versioned', be_versioned);
it.should('contain three thingamajigs when initialized', have_three_thingamajigs);
}
private static function be_versioned():void
{
var it:Thing = Spec.describe('a Thing');
it.expects(Widget.version).toPatternMatch('(%d+).(%d+).(%d+)', 3);
}
it.should('exist', function() {
it.expects(MySpec).not.toBeNull();
});
private static function have_three_thingamajigs():void
{
// assert before array access to avoid out-of-bounds error
it.asserts(Widget.thingamajigs.length).isEqualTo(3).or('Widget initialized without three thingamajigs');
it.expects(Widget.thingamajigs[2]).isTypeOf(Sprocket).or('Third thingamajig not a Sprocket');
}
}
Expand All @@ -70,25 +96,44 @@ package

> **TIP**: use [SpecExecutor][SpecExecutor.ls]; it has convenience methods to set reporter formats and seed values. See [SpecTest][SpecTest.ls] for an example.
### matchers
### expectations

spec-ls provides a set of expectation tests for specifying behavior:

spec-ls has a basic set of expectation phrases for specifying behavior:
`it.expects(value:Object)`

* `toBeA(type:Type)`
* `toBeEmpty()`
* `toBeFalsey()` / `toBeTruthy()`
* `toBeGreaterThan(value2:Number)` / `toBeLessThan(value2:Number)`
* `toBeNaN()`
* `toBeNull()`
* `toBePlusOrMinus(absoluteDelta:Number).from(value2:Number)`
* `toContain(value2:Object)`
* `toEndWith(value2:String)` / `toStartWith(value2:String)`
* `toEqual(value2:Object)`
* `toPatternMatch(value2:String, matches:Number=1)`
* `.toBeA(type:Type)`
* `.toBeEmpty()`
* `.toBeFalsey()` / `toBeTruthy()`
* `.toBeGreaterThan(value2:Number)` / `toBeLessThan(value2:Number)`
* `.toBeNaN()`
* `.toBeNull()`
* `.toBePlusOrMinus(absoluteDelta:Number).from(value2:Number)`
* `.toContain(value2:Object)`
* `.toEndWith(value2:String)` / `toStartWith(value2:String)`
* `.toEqual(value2:Object)`
* `.toPatternMatch(value2:String, matches:Number=1)`

they are defined in [Matcher.ls][Matcher.ls];
you can see them used in the specifications for spec-ls itself: [ExpectationSpec][ExpectationSpec.ls]

### assertions

spec-ls provides a set of assertion tests for mandating test pre-conditions and aborting on violation:

`it.asserts(value:Object)`

* `.isNotNaN().or('value was NaN')`
* `.isNull().or('value was not null')` / `.isNotNull().or('value was null')`
* `.isEmpty().or('value was not empty')` / `.isNotEmpty().or('value was empty')`
* `.isEqualTo(value2).or('value was not equal to value2')` / `.isNotEqualTo(value2).or('value was equal to value2')`
* `.isGreaterThan(value2).or('value was not greater than value2')`
* `.isLessThan(value2).or('value was not less than value2')`
* `.isTypeOf(type).or('value was not a kind of type')`

they are defined in [Assertion.ls][Assertion.ls];
you can see them used in the specifications for spec-ls itself: [AssertionSpec][AssertionSpec.ls]

### reporters

spec-ls ships with three reporters:
Expand All @@ -101,7 +146,7 @@ spec-ls ships with three reporters:

### random seed

by default, Spec will execute tests in a different random order every time, to guard against accidental order dependencies.
by default, Spec will execute tests in a different random order every time, to guard against hidden dependencies.

to reproduce the order of a specific run, pass in the same seed value to `Spec.execute()`:

Expand Down Expand Up @@ -140,6 +185,8 @@ this will build the Spec library, install it in the currently configured sdk, bu
Pull requests are welcome!


[Assertion.ls]: lib/src/pixeldroid/bdd/Assertion.ls "Assertion.ls"
[AssertionSpec.ls]: test/src/spec/AssertionSpec.ls "AssertionSpec.ls"
[ExpectationSpec.ls]: test/src/spec/ExpectationSpec.ls "ExpectationSpec.ls"
[loom-sdk]: https://github.com/LoomSDK/LoomSDK "a native mobile app and game framework"
[loomtasks]: https://github.com/pixeldroid/loomtasks "Rake tasks for working with loomlibs"
Expand Down
4 changes: 2 additions & 2 deletions lib/src/Spec.build
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "Spec",
"version": "1.3.1",
"version": "2.0.0",
"outputDir": "./build",
"references": [
"System"
],
"modules": [
{
"name": "Spec",
"version": "1.3.1",
"version": "2.0.0",
"sourcePath": [
"."
]
Expand Down
4 changes: 2 additions & 2 deletions lib/src/pixeldroid/ansi/ANSI.ls
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ package pixeldroid.ansi
private function addSGR(n:Number, args:String=''):ANSI
{
// SGR codes follow the CSIx[;y][;z]m format
_string = _string.concat([code_begin, n, args, sgr_end]);
_string += (code_begin +n +args +sgr_end);

return this;
}

private function addCode(code:String):ANSI
{
// regular codes are just CSIx format
_string = _string.concat([code_begin, code]);
_string += (code_begin +code);

return this;
}
Expand Down
173 changes: 173 additions & 0 deletions lib/src/pixeldroid/bdd/Assertion.ls
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@

package pixeldroid.bdd
{
import system.Debug;
import system.Process;
import system.reflection.Type;

import pixeldroid.bdd.Does;
import pixeldroid.bdd.models.Requirement;
import pixeldroid.platform.CallUtils;


public class Assertion
{
private var value1:Object;
private var source:String;
private var line:Number;
private var method:String;

public static const defaultAsserter:Asserter = new ExitAsserter();
public static var asserter:Asserter = defaultAsserter;


public function Assertion(context:Requirement, value:Object)
{
value1 = value;
source = context.currentCallInfo.source;
line = context.currentCallInfo.line;
method = context.currentCallInfo.method.getName();
}

protected function getAsserter(condition:Boolean):Asserter
{
var a:Asserter = asserter.getType().getConstructor().invoke() as Asserter;
a.init(condition, source, line, method);

return a;
}


public function isNotNaN():Asserter
{
var condition:Boolean = (!isNaN(value1 as Number));
return getAsserter(condition);
}

public function isNull():Asserter
{
var condition:Boolean = (value1 == null);
return getAsserter(condition);
}

public function isNotNull():Asserter
{
var condition:Boolean = (value1 != null);
return getAsserter(condition);
}

public function isEmpty():Asserter
{
var condition:Boolean;

if (Does.typeMatch(value1, String))
{
var s:String = value1 as String;
condition = (s.length == 0);
}
else if (Does.typeMatch(value1, Vector))
{
var vector:Vector = value1 as Vector;
condition = (vector.length == 0);
}
else
{
(getAsserter(false)).or('value provided is not a container');
}

return getAsserter(condition);
}

public function isNotEmpty():Asserter
{
var condition:Boolean;

if (Does.typeMatch(value1, String))
{
var s:String = value1 as String;
condition = (s.length > 0);
}
else if (Does.typeMatch(value1, Vector))
{
var vector:Vector = value1 as Vector;
condition = (vector.length > 0);
}
else
{
(getAsserter(false)).or('value provided is not a container');
}

return getAsserter(condition);
}

public function isEqualTo(value2:Object):Asserter
{
var condition:Boolean = (value1 == value2);
return getAsserter(condition);
}

public function isNotEqualTo(value2:Object):Asserter
{
var condition:Boolean = (value1 != value2);
return getAsserter(condition);
}

public function isGreaterThan(value2:Number):Asserter
{
var condition:Boolean = (value1 > value2);
return getAsserter(condition);
}

public function isLessThan(value2:Number):Asserter
{
var condition:Boolean = (value1 < value2);
return getAsserter(condition);
}

public function isTypeOf(type:Type):Asserter
{
var condition:Boolean = (Does.typeMatch(value1, type) || Does.subtypeMatch(value1, type));
return getAsserter(condition);
}


}


public interface Asserter
{
function init(condition:Boolean, source:String, line:Number, method:String):void;
function or(message:String):Boolean;
}

public class ExitAsserter implements Asserter
{
private static const FAILURE:Number = 1;

private var condition:Boolean;
private var source:String;
private var line:Number;
private var method:String;

public function init(condition:Boolean, source:String, line:Number, method:String):void
{
this.condition = condition;
this.source = source;
this.line = line;
this.method = method;
}

public function or(message:String):Boolean
{
if (!condition)
{
trace('runtime assertion failed:', message);
trace(source +'(' +method +'):' +line);

Process.exit(FAILURE);
}

return true;
}
}
}
Loading

0 comments on commit bf8d405

Please sign in to comment.