Skip to content
This repository has been archived by the owner on Jan 16, 2023. It is now read-only.

Refactor to use type-mapping instead of if/else cascade #1

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ php:

before_script:
- wget 'https://github.com/xp-framework/xp-runners/releases/download/v5.0.0/setup' -O - | php
- wget 'https://github.com/xp-framework/core/releases/download/v6.0.0alpha6/xp-rt-6.0.0alpha6.xar'
- wget 'https://github.com/xp-framework/core/releases/download/v6.0.0beta1/xp-rt-6.0.0beta1.xar'
- ls -1 *.xar > boot.pth
- echo "[runtime]" >> xp.ini
- echo "date.timezone=Europe/Berlin" >> xp.ini
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/ArrayPrimitiveValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class ArrayPrimitiveValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return sizeof($this->value) === $size; },
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/ArrayValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class ArrayValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return $value->length === $size; },
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/MapPrimitiveValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class MapPrimitiveValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return sizeof($this->value) === $size; },
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/MapValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class MapValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return $value->size === $size; },
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/StringPrimitiveValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class StringPrimitiveValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return strlen($value) === $size;; },
Expand Down
2 changes: 2 additions & 0 deletions src/main/php/unittest/assert/StringValue.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class StringValue extends Value {

static function __static() { }

public function hasSize($size) {
return $this->is(new Match(
function($value) use($size) { return $value->length() === $size; },
Expand Down
102 changes: 102 additions & 0 deletions src/main/php/unittest/assert/TypeMap.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php namespace unittest\assert;

use lang\Type;

/**
* Maps types
*
* @test xp://unittest.assert.unittest.TypeMapTest
*/
class TypeMap extends \lang\Object implements \ArrayAccess {
protected $types= [];

/**
* Finds a given type and returns the key, or NULL if nothing is found
*
* @param lang.Type $type
* @param string
*/
protected function find(Type $type) {
$class= $type->getName();
if (isset($this->types[$class])) {
return $class;
} else {
foreach ($this->types as $mapped => $to) {
if (Type::forName($mapped)->isAssignableFrom($type)) return $mapped;
}
}
return null;
}

/**
* Maps a given type to a given value
*
* @param lang.Type $type
* @param var $value
* @return self
*/
public function map(Type $type, $value) {
$this->types[$type->getName()]= $value;
return $this;
}

/**
* Gets mapping for a given type
*
* @param lang.Type $type
* @param var $default
* @return var $value
*/
public function get($type, $default= null) {
if ($found= $this->find($type)) {
return $this->types[$found];
} else {
return $default;
}
}

/**
* list[]= overloading
*
* @param lang.Type $type
* @param var $value
* @throws lang.IllegalArgumentException if key is neither a string (set) nor NULL (add)
*/
public function offsetSet($type, $value) {
$this->types[$type->getName()]= $value;
}

/**
* = list[] overloading
*
* @param lang.Type $type
* @return var
* @throws lang.IndexOutOfBoundsException if key does not exist
*/
public function offsetGet($type) {
if ($found= $this->find($type)) {
return $this->types[$found];
} else {
raise('lang.IndexOutOfBoundsException', 'No element for type '.$type);
}
}

/**
* isset() overloading
*
* @param lang.Type $type
* @return bool
*/
public function offsetExists($type) {
return null !== $this->find($type);
}

/**
* unset() overloading
*
* @param lang.Type $type
*/
public function offsetUnset($type) {
unset($this->types[$type->getName()]);
}
}
40 changes: 21 additions & 19 deletions src/main/php/unittest/assert/Value.class.php
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
<?php namespace unittest\assert;

use lang\Type;
use lang\XPClass;
use lang\ArrayType;
use lang\MapType;
use lang\Primitive;
use util\Objects;
use unittest\AssertionFailedError;
use lang\types\ArrayList;
use lang\types\ArrayMap;
use lang\types\String;

class Value extends \lang\Object {
protected static $types;
protected $value;
protected $verify= [];

static function __static() {
$ctor= Type::forName('function(var): unittest.assert.Value');
self::$types= (new TypeMap())
->map(Primitive::$STRING, $ctor->cast('unittest\assert\StringPrimitiveValue::new'))
->map(ArrayType::forName('var[]'), $ctor->cast('unittest\assert\ArrayPrimitiveValue::new'))
->map(MapType::forName('[:var]'), $ctor->cast('unittest\assert\MapPrimitiveValue::new'))
->map(XPClass::forName('lang.types.String'), $ctor->cast('unittest\assert\StringValue::new'))
->map(XPClass::forName('lang.types.ArrayList'), $ctor->cast('unittest\assert\ArrayValue::new'))
->map(XPClass::forName('lang.types.ArrayMap'), $ctor->cast('unittest\assert\MapValue::new'))
->map(Type::$VAR, $ctor->cast('unittest\assert\Value::new'))
;
}

/**
* Creates a new instance
*
* @param var $value
*/
protected function __construct($value) {
public function __construct($value) {
$this->value= $value;
}

Expand All @@ -27,21 +42,8 @@ protected function __construct($value) {
* @return unittest.assert.Value
*/
public static function of($value) {
if (is_array($value) && 0 === key($value)) {
return new ArrayPrimitiveValue($value);
} else if (is_array($value)) {
return new MapPrimitiveValue($value);
} else if (is_string($value)) {
return new StringPrimitiveValue($value);
} else if ($value instanceof ArrayList) {
return new ArrayValue($value);
} else if ($value instanceof ArrayMap) {
return new MapValue($value);
} else if ($value instanceof String) {
return new StringValue($value);
} else {
return new Value($value);
}
$ctor= self::$types[typeof($value)];
return $ctor($value);
}

/**
Expand Down
105 changes: 105 additions & 0 deletions src/test/php/unittest/assert/unittest/TypeMapTest.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php namespace unittest\assert\unittest;

use unittest\assert\TypeMap;
use lang\XPClass;
use lang\Type;

/**
* Tests the `TypeMap` class.
*/
class TypeMapTest extends \unittest\TestCase {
const FOUND = 'found';

#[@test]
public function can_create() {
new TypeMap();
}

#[@test]
public function test_non_existant_returns_false() {
$map= new TypeMap();
$this->assertFalse(isset($map[XPClass::forName('lang.Object')]));
}

#[@test]
public function test_direct_type_returns_true() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertTrue(isset($map[XPClass::forName('lang.Object')]));
}

#[@test]
public function test_parent_type_returns_true() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertTrue(isset($map[XPClass::forName('unittest.TestCase')]));
}

#[@test]
public function test_interface_type_returns_true() {
$map= (new TypeMap())->map(XPClass::forName('lang.Generic'), self::FOUND);
$this->assertTrue(isset($map[XPClass::forName('unittest.TestCase')]));
}

#[@test]
public function lookup_non_existant_returns_null() {
$this->assertEquals(null, (new TypeMap())->get(XPClass::forName('lang.Object')));
}

#[@test, @expect('lang.IndexOutOfBoundsException')]
public function read_non_existant_throws_exception() {
$this->assertEquals(null, (new TypeMap())[XPClass::forName('lang.Object')]);
}

#[@test]
public function lookup_direct_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertEquals(self::FOUND, $map->get(XPClass::forName('lang.Object')));
}

#[@test]
public function read_direct_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertEquals(self::FOUND, $map[XPClass::forName('lang.Object')]);
}

#[@test]
public function lookup_parent_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertEquals(self::FOUND, $map->get(XPClass::forName('unittest.TestCase')));
}

#[@test]
public function read_parent_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Object'), self::FOUND);
$this->assertEquals(self::FOUND, $map[XPClass::forName('unittest.TestCase')]);
}

#[@test]
public function lookup_interface_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Generic'), self::FOUND);
$this->assertEquals(self::FOUND, $map->get(XPClass::forName('unittest.TestCase')));
}

#[@test]
public function read_interface_type() {
$map= (new TypeMap())->map(XPClass::forName('lang.Generic'), self::FOUND);
$this->assertEquals(self::FOUND, $map[XPClass::forName('unittest.TestCase')]);
}

#[@test]
public function test_var_type() {
$map= (new TypeMap())->map(Type::$VAR, self::FOUND);
$this->assertTrue(isset($map[XPClass::forName('lang.Object')]));
}

#[@test]
public function lookup_var_type() {
$map= (new TypeMap())->map(Type::$VAR, self::FOUND);
$this->assertEquals(self::FOUND, $map->get(XPClass::forName('lang.Object')));
}

#[@test]
public function read_var_type() {
$map= (new TypeMap())->map(Type::$VAR, self::FOUND);
$this->assertEquals(self::FOUND, $map[XPClass::forName('lang.Object')]);
}
}