Skip to content

Commit

Permalink
Merge pull request #17 from aldas/doubleWord_32bit
Browse files Browse the repository at this point in the history
fix DoubleWord->getUInt32 return type problem on 32bit arch
  • Loading branch information
aldas committed Jan 9, 2019
2 parents f47a2a6 + fc66579 commit d3ba15b
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 29 deletions.
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ php:
- 7.0
- 7.1
- 7.2
- 7.3

env:
- ARCH=64bit
- ARCH=32bit

before_install:
- travis_retry composer self-update
Expand All @@ -18,7 +23,8 @@ install:
- travis_retry composer update --no-interaction

script:
- vendor/bin/phpunit --testsuite 'unit-tests' --coverage-clover=coverage.xml
- if [ "$ARCH" = "32bit" ]; then ./docker-run-test.sh $(phpenv version-name); fi
- if [ "$ARCH" = "64bit" ]; then ./vendor/bin/phpunit --testsuite 'unit-tests' --coverage-clover=coverage.xml; fi

after_success:
- bash <(curl -s https://codecov.io/bash)
21 changes: 21 additions & 0 deletions docker-run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash

PHP_VERSION=${1:-7.1}
PHPUNIT_OPTS=${*:2}

# if ARCH env variable is not set then use 64bit as default
if [[ -z "${ARCH}" ]]; then
ARCH=64bit
fi

if [[ "$ARCH" == "64bit" ]]; then
IMAGE=php
elif [[ "$ARCH" == "32bit" ]]; then
IMAGE=i386/php
else
echo Unknown arch value: ${ARCH}
exit 1
fi

echo Using ${ARCH} PHP-${PHP_VERSION}
docker run -i -v "${PWD}:/code" -w /code/ ${IMAGE}:${PHP_VERSION}-cli-alpine php /code/vendor/bin/phpunit ${PHPUNIT_OPTS}
7 changes: 5 additions & 2 deletions src/Packet/DoubleWord.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ protected function getByteLength(): int

/**
* @param int $endianness byte and word order for modbus binary data
* @return int
*
* NB: On 32bit php and having highest bit set method will return float instead of int value. This is due 32bit php supports only 32bit signed integers
*
* @return int|float
* @throws \RuntimeException
*/
public function getUInt32(int $endianness = null): int
public function getUInt32(int $endianness = null)
{
return Types::parseUInt32($this->getData(), $endianness);
}
Expand Down
4 changes: 0 additions & 4 deletions src/Utils/Types.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,10 +543,6 @@ private static function toInt32Internal($data, $endianness = null): string
*/
public static function toInt64($data, int $toEndian = null): string
{
if (PHP_INT_SIZE !== 8) {
throw new InvalidArgumentException('64-bit format codes are not available for 32-bit versions of PHP');
}

$words = [
($data >> 48) & 0xFFFF,
($data >> 32) & 0xFFFF,
Expand Down
19 changes: 16 additions & 3 deletions tests/unit/Composer/Read/ReadAddressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


use ModbusTcpClient\Composer\Address;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersResponse;
use ModbusTcpClient\Composer\Read\ReadAddress;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersResponse;
use PHPUnit\Framework\TestCase;

class ReadAddressTest extends TestCase
Expand Down Expand Up @@ -103,7 +103,12 @@ function (\Exception $exception, $address, $response) use (&$errCbAddress, &$err

$value = $address->extract($responsePacket);

$this->assertEquals('ModbusTcpClient\Exception\OverflowException_64-bit PHP supports only up to 63-bit signed integers. Current input has 64th bit set and overflows. Hex: ffffffffffffffff', $value);
$error = 'ModbusTcpClient\Exception\OverflowException_64-bit PHP supports only up to 63-bit signed integers. Current input has 64th bit set and overflows. Hex: ffffffffffffffff';
if (PHP_INT_SIZE === 4) {
$error = 'ModbusTcpClient\Exception\ParseException_64-bit format codes are not available for 32-bit versions of PHP';
}
$this->assertEquals($error, $value);

$this->assertEquals($responsePacket, $errCbResponse);
$this->assertEquals($address, $errCbAddress);
}
Expand All @@ -116,7 +121,7 @@ public function testErrorCallbackWhenCbFails()
$errCbResponse = null;
$address = new ReadAddress(
0,
Address::TYPE_UINT64,
Address::TYPE_INT16,
null,
function () {
throw new \RuntimeException('catch me');
Expand Down Expand Up @@ -187,6 +192,10 @@ public function testExtractFloat()

public function testExtractUInt64()
{
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped('32-bit version of PHP');
}

$responsePacket = new ReadHoldingRegistersResponse("\x08\xFF\xFF\x7F\xFF\x00\x00\x00\x00", 3, 33152);
$address = new ReadAddress(0, Address::TYPE_UINT64);

Expand All @@ -197,6 +206,10 @@ public function testExtractUInt64()

public function testExtractInt64()
{
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped('32-bit version of PHP');
}

$responsePacket = new ReadHoldingRegistersResponse("\x08\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 3, 33152);
$address = new ReadAddress(0, Address::TYPE_INT64);

Expand Down
10 changes: 7 additions & 3 deletions tests/unit/Composer/Write/WriteAddressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ public function testGetValue()
/**
* @dataProvider toBinaryProvider
*/
public function testToBinary(string $expectedBinaryString, string $type, $value, $expectedException = null)
public function testToBinary(string $expectedBinaryString, string $type, $value, $expectedException = null, $skipOn32Bit = false)
{
if ($skipOn32Bit && PHP_INT_SIZE === 4) {
$this->markTestSkipped('32-bit version of PHP');
}

if ($expectedException !== null) {
$this->expectException($expectedException);
}
Expand All @@ -37,8 +41,8 @@ public function toBinaryProvider()
'uint16 to binary' => ["\xFF\xFF", Address::TYPE_UINT16, 65535],
'int32 to binary' => ["\x00\x00\x80\x00", Address::TYPE_INT32, -2147483648],
'uint32 to binary' => ["\x00\x01\x00\x00", Address::TYPE_UINT32, 1],
'int64 to binary' => ["\x00\x00\x00\x00\x00\x00\x80\x00", Address::TYPE_INT64, -9223372036854775808],
'uint64 to binary' => ["\x00\x01\x00\x00\x00\x00\x00\x00", Address::TYPE_UINT64, 1],
'int64 to binary' => ["\x00\x00\x00\x00\x00\x00\x80\x00", Address::TYPE_INT64, -9223372036854775808, null, true],
'uint64 to binary' => ["\x00\x01\x00\x00\x00\x00\x00\x00", Address::TYPE_UINT64, 1, null, true],
'float to binary' => ["\xcc\xcd\x3f\xec", Address::TYPE_FLOAT, 1.85],
'string to binary' => ["\x00\x6E\x65\x72\xF8\x53", Address::TYPE_STRING, 'Søren', \ModbusTcpClient\Exception\InvalidArgumentException::class],
];
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/Packet/DoubleWordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


use ModbusTcpClient\Packet\DoubleWord;
use ModbusTcpClient\Utils\Endian;
use PHPUnit\Framework\TestCase;

class DoubleWordTest extends TestCase
Expand Down Expand Up @@ -31,6 +32,23 @@ public function testShouldGetUInt32()
$this->assertEquals(2147483647, $dWord->getUInt32());
}

public function testShouldGetUInt32Types()
{
$dWordUpperLimit = new DoubleWord("\xFF\xFF\xFF\xFF");
$dWord = new DoubleWord("\x00\x00\x00\x01");

if (PHP_INT_SIZE === 8) {
$this->assertTrue(is_int($dWordUpperLimit->getUInt32(Endian::BIG_ENDIAN)));
$this->assertTrue(is_int($dWord->getUInt32(Endian::BIG_ENDIAN)));
} else {
$this->assertTrue(is_float($dWordUpperLimit->getUInt32(Endian::BIG_ENDIAN)));
$this->assertTrue(is_int($dWord->getUInt32(Endian::BIG_ENDIAN)));
}

$this->assertEquals(4294967295, $dWordUpperLimit->getUInt32(Endian::BIG_ENDIAN));
$this->assertEquals(1, $dWord->getUInt32(Endian::BIG_ENDIAN));
}

public function testShouldGetInt32()
{
$dWord = new DoubleWord("\x00\x00\x80\x00");
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/Packet/QuadWordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public function testShouldNotConstructFromLongerData()
*/
public function testShouldOverflow()
{
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped('64-bit format codes are not available for 32-bit versions of PHP');
}

// 64-bit PHP supports only up to 63-bit signed integers. Parsing this value results '-1' which is overflow
$quadWord = new QuadWord("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");

Expand All @@ -40,13 +44,21 @@ public function testShouldOverflow()

public function testShouldGetUInt64()
{
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped('64-bit format codes are not available for 32-bit versions of PHP');
}

$quadWord = new QuadWord("\xFF\xFF\x7F\xFF\x00\x00\x00\x00");

$this->assertEquals(2147483647, $quadWord->getUInt64(Endian::BIG_ENDIAN_LOW_WORD_FIRST));
}

public function testShouldGetInt64()
{
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped('64-bit format codes are not available for 32-bit versions of PHP');
}

$quadWord = new QuadWord("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");

$this->assertEquals(-1, $quadWord->getInt64(Endian::BIG_ENDIAN_LOW_WORD_FIRST));
Expand Down
40 changes: 24 additions & 16 deletions tests/unit/Utils/TypesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ public function toUint16Provider()
/**
* @dataProvider toInt32Provider
*/
public function testShouldEncodeToBinaryInt32(string $expectedBinaryString, int $integer, int $endian, $expectedException = null)
public function testShouldEncodeToBinaryInt32(string $expectedBinaryString, $integer, int $endian, $expectedException = null)
{
if ($expectedException !== null) {
$this->expectException($expectedException);
Expand Down Expand Up @@ -530,7 +530,7 @@ public function toInt32Provider()
/**
* @dataProvider toUint32Provider
*/
public function testShouldEncodeToBinaryUint32(string $expectedBinaryString, int $integer, int $endian, $expectedException = null)
public function testShouldEncodeToBinaryUint32(string $expectedBinaryString, $integer, int $endian, $expectedException = null)
{
if ($expectedException !== null) {
$this->expectException($expectedException);
Expand Down Expand Up @@ -562,35 +562,43 @@ public function toUint32Provider()
/**
* @dataProvider toInt64Provider
*/
public function testShouldEncodeToBinaryInt64(string $expectedBinaryString, int $integer, int $endian)
public function testShouldEncodeToBinaryInt64(string $expectedBinaryString, $integer, int $endian, $skipOn32Bit = false)
{
if ($skipOn32Bit && PHP_INT_SIZE === 4) {
$this->markTestSkipped('32-bit version of PHP');
}

$this->assertEquals($expectedBinaryString, Types::toInt64($integer, $endian));
}

public function toInt64Provider()
{
return [
'BigEndianLowWordFirst: toInt64 1' => ["\x00\x01\x00\x00\x00\x00\x00\x00", 1, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 2923517522' => ["\x56\x52\xAE\x41\x00\x00\x00\x0", 2923517522, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 2923517522' => ["\x56\x52\xAE\x41\x00\x00\x00\x0", 2923517522, Endian::BIG_ENDIAN_LOW_WORD_FIRST, true],
'BigEndianLowWordFirst: toInt64 67305985' => ["\x02\x01\x04\x03\x00\x00\x00\x00", 67305985, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 9223372036854775807' => ["\xFF\xFF\xFF\xFF\xFF\xFF\x7F\xFF", 9223372036854775807, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 -9223372036854775808' => ["\x00\x00\x00\x00\x00\x00\x80\x00", -9223372036854775808, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 9223372036854775807' => ["\xFF\xFF\xFF\xFF\xFF\xFF\x7F\xFF", 9223372036854775807, Endian::BIG_ENDIAN_LOW_WORD_FIRST, true],
'BigEndianLowWordFirst: toInt64 -9223372036854775808' => ["\x00\x00\x00\x00\x00\x00\x80\x00", -9223372036854775808, Endian::BIG_ENDIAN_LOW_WORD_FIRST, true],

'BigEndian: toInt64 1' => ["\x00\x00\x00\x00\x00\x00\x00\x01", 1, Endian::BIG_ENDIAN],
'BigEndian: toInt64 2923517522' => ["\x00\x00\x00\x00\xAE\x41\x56\x52", 2923517522, Endian::BIG_ENDIAN],
'BigEndian: toInt64 2923517522' => ["\x00\x00\x00\x00\xAE\x41\x56\x52", 2923517522, Endian::BIG_ENDIAN, true],
'BigEndian: toInt64 67305985' => ["\x00\x00\x00\x00\x04\x03\x02\x01", 67305985, Endian::BIG_ENDIAN],
'BigEndian: toInt64 9223372036854775807' => ["\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::BIG_ENDIAN],
'BigEndian: toInt64 -9223372036854775808' => ["\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808, Endian::BIG_ENDIAN],
'BigEndian: toInt64 9223372036854775807' => ["\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::BIG_ENDIAN, true],
'BigEndian: toInt64 -9223372036854775808' => ["\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808, Endian::BIG_ENDIAN, true],

'LittleEndian: toInt64 -9223372036854775808' => ["\x00\x80\x00\x00\x00\x00\x00\x00", -9223372036854775808, Endian::LITTLE_ENDIAN],
'LittleEndian: toInt64 -9223372036854775808' => ["\x00\x80\x00\x00\x00\x00\x00\x00", -9223372036854775808, Endian::LITTLE_ENDIAN, true],
];
}

/**
* @dataProvider toUint64Provider
*/
public function testShouldEncodeToBinaryUint64(string $expectedBinaryString, int $integer, int $endian, $expectedException = null)
public function testShouldEncodeToBinaryUint64(string $expectedBinaryString, $integer, int $endian, $expectedException = null, $skipOn32Bit = false)
{
if ($skipOn32Bit && PHP_INT_SIZE === 4) {
$this->markTestSkipped('32-bit version of PHP');
}

if ($expectedException !== null) {
$this->expectException($expectedException);
}
Expand All @@ -601,19 +609,19 @@ public function toUint64Provider()
{
return [
'BigEndianLowWordFirst: toInt64 1' => ["\x00\x01\x00\x00\x00\x00\x00\x00", 1, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 2923517522' => ["\x56\x52\xAE\x41\x00\x00\x00\x0", 2923517522, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 2923517522' => ["\x56\x52\xAE\x41\x00\x00\x00\x0", 2923517522, Endian::BIG_ENDIAN_LOW_WORD_FIRST, null, true],
'BigEndianLowWordFirst: toInt64 67305985' => ["\x02\x01\x04\x03\x00\x00\x00\x00", 67305985, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 9223372036854775807' => ["\xFF\xFF\xFF\xFF\xFF\xFF\x7F\xFF", 9223372036854775807, Endian::BIG_ENDIAN_LOW_WORD_FIRST],
'BigEndianLowWordFirst: toInt64 9223372036854775807' => ["\xFF\xFF\xFF\xFF\xFF\xFF\x7F\xFF", 9223372036854775807, Endian::BIG_ENDIAN_LOW_WORD_FIRST, null, true],

'BigEndianLowWordFirst: toInt64 -1 (underflow)' => ['', -1, Endian::BIG_ENDIAN_LOW_WORD_FIRST, \ModbusTcpClient\Exception\OverflowException::class],

'BigEndian: toInt64 1' => ["\x00\x00\x00\x00\x00\x00\x00\x01", 1, Endian::BIG_ENDIAN],
'BigEndian: toInt64 2923517522' => ["\x00\x00\x00\x00\xAE\x41\x56\x52", 2923517522, Endian::BIG_ENDIAN],
'BigEndian: toInt64 2923517522' => ["\x00\x00\x00\x00\xAE\x41\x56\x52", 2923517522, Endian::BIG_ENDIAN, null, true],
'BigEndian: toInt64 67305985' => ["\x00\x00\x00\x00\x04\x03\x02\x01", 67305985, Endian::BIG_ENDIAN],
'BigEndian: toInt64 9223372036854775807' => ["\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::BIG_ENDIAN],
'BigEndian: toInt64 9223372036854775807' => ["\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::BIG_ENDIAN, null, true],
'BigEndian: toInt64 -1 (underflow)' => ['', -1, Endian::BIG_ENDIAN, \ModbusTcpClient\Exception\OverflowException::class],

'LittleEndian: toInt64 9223372036854775807' => ["\xFF\x7F\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::LITTLE_ENDIAN],
'LittleEndian: toInt64 9223372036854775807' => ["\xFF\x7F\xFF\xFF\xFF\xFF\xFF\xFF", 9223372036854775807, Endian::LITTLE_ENDIAN, null, true],
];
}

Expand Down

0 comments on commit d3ba15b

Please sign in to comment.