Merge branch 'development' into feature/TTD-2140/DB-IO-SQLite
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
base=$(pwd)"/";
|
||||
# must be run in ${base}
|
||||
cd $base || exit;
|
||||
${base}tools/phan --progress-bar -C --analyze-twice;
|
||||
#PHAN_DISABLE_XDEBUG_WARN=1;${base}tools/phan --progress-bar -C --analyze-twice
|
||||
PHAN_DISABLE_XDEBUG_WARN=1;${base}vendor/bin/phan --progress-bar -C --analyze-twice
|
||||
cd ~ || exit;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
base=$(pwd)"/";
|
||||
# must be run in ${base}
|
||||
cd $base || exit;
|
||||
${base}tools/phpstan;
|
||||
|
||||
@@ -23,7 +23,7 @@ EOF
|
||||
}
|
||||
|
||||
# set base variables
|
||||
BASE_PATH="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
BASE_PATH=$(pwd)"/";
|
||||
PHPUNIT_CONFIG="${BASE_PATH}phpunit.xml";
|
||||
PHP_BIN_PATH=$(which php);
|
||||
if [ -z "${PHP_BIN_PATH}" ]; then
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
// This code was created by Claude Sonnet 4
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
class CoreLibsCombinedArrayHandlerFindArraysMissingKeyTest extends TestCase
|
||||
{
|
||||
private const DATA_SEPARATOR = ':'; // Updated to match your class's separator
|
||||
|
||||
/**
|
||||
* Test finding missing single key when searching by value without specific key
|
||||
*/
|
||||
public function testFindMissingSingleKeyWithValueSearch()
|
||||
{
|
||||
$array = [
|
||||
'item1' => [
|
||||
'name' => 'John',
|
||||
'age' => 25
|
||||
// missing 'email' key
|
||||
],
|
||||
'item2' => [
|
||||
'name' => 'Jane',
|
||||
'age' => 30,
|
||||
'email' => 'jane@example.com'
|
||||
],
|
||||
'item3' => [
|
||||
'name' => 'John', // same value as item1
|
||||
'age' => 35,
|
||||
'email' => 'john2@example.com'
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($array['item1'], $result[0]['content']);
|
||||
$this->assertEquals('item1', $result[0]['path']);
|
||||
$this->assertEquals(['email'], $result[0]['missing_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test finding missing single key when searching by specific key-value pair
|
||||
*/
|
||||
public function testFindMissingSingleKeyWithKeyValueSearch()
|
||||
{
|
||||
$array = [
|
||||
'user1' => [
|
||||
'id' => 1,
|
||||
'name' => 'Alice'
|
||||
// missing 'status' key
|
||||
],
|
||||
'user2' => [
|
||||
'id' => 2,
|
||||
'name' => 'Bob',
|
||||
'status' => 'active'
|
||||
],
|
||||
'user3' => [
|
||||
'id' => 1, // same id as user1
|
||||
'name' => 'Charlie',
|
||||
'status' => 'inactive'
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 1, 'status', 'id');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($array['user1'], $result[0]['content']);
|
||||
$this->assertEquals('user1', $result[0]['path']);
|
||||
$this->assertEquals(['status'], $result[0]['missing_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test finding missing multiple keys
|
||||
*/
|
||||
public function testFindMissingMultipleKeys()
|
||||
{
|
||||
$array = [
|
||||
'record1' => [
|
||||
'name' => 'Test',
|
||||
'value' => 100
|
||||
// missing both 'date' and 'status' keys
|
||||
],
|
||||
'record2' => [
|
||||
'name' => 'Test',
|
||||
'value' => 200,
|
||||
'date' => '2023-01-01'
|
||||
// missing 'status' key
|
||||
],
|
||||
'record3' => [
|
||||
'name' => 'Test',
|
||||
'value' => 300,
|
||||
'date' => '2023-01-02',
|
||||
'status' => 'complete'
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'Test', ['date', 'status']);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
|
||||
// First result should be record1 missing both keys
|
||||
$this->assertEquals($array['record1'], $result[0]['content']);
|
||||
$this->assertEquals('record1', $result[0]['path']);
|
||||
$this->assertContains('date', $result[0]['missing_key']);
|
||||
$this->assertContains('status', $result[0]['missing_key']);
|
||||
$this->assertCount(2, $result[0]['missing_key']);
|
||||
|
||||
// Second result should be record2 missing status key
|
||||
$this->assertEquals($array['record2'], $result[1]['content']);
|
||||
$this->assertEquals('record2', $result[1]['path']);
|
||||
$this->assertEquals(['status'], $result[1]['missing_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with nested arrays
|
||||
*/
|
||||
public function testFindMissingKeyInNestedArrays()
|
||||
{
|
||||
$array = [
|
||||
'section1' => [
|
||||
'items' => [
|
||||
'item1' => [
|
||||
'name' => 'Product A',
|
||||
'price' => 99.99
|
||||
// missing 'category' key
|
||||
],
|
||||
'item2' => [
|
||||
'name' => 'Product B',
|
||||
'price' => 149.99,
|
||||
'category' => 'electronics'
|
||||
]
|
||||
]
|
||||
],
|
||||
'section2' => [
|
||||
'data' => [
|
||||
'name' => 'Product A', // same name as nested item
|
||||
'category' => 'books'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'Product A', 'category');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($array['section1']['items']['item1'], $result[0]['content']);
|
||||
$this->assertEquals('section1:items:item1', $result[0]['path']);
|
||||
$this->assertEquals(['category'], $result[0]['missing_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when no arrays are missing the required key
|
||||
*/
|
||||
public function testNoMissingKeys()
|
||||
{
|
||||
$array = [
|
||||
'item1' => [
|
||||
'name' => 'John',
|
||||
'email' => 'john@example.com'
|
||||
],
|
||||
'item2' => [
|
||||
'name' => 'Jane',
|
||||
'email' => 'jane@example.com'
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when search value is not found in any array
|
||||
*/
|
||||
public function testSearchValueNotFound()
|
||||
{
|
||||
$array = [
|
||||
'item1' => [
|
||||
'name' => 'John',
|
||||
'age' => 25
|
||||
],
|
||||
'item2' => [
|
||||
'name' => 'Jane',
|
||||
'age' => 30
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'Bob', 'email');
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with different data types for search value
|
||||
*/
|
||||
public function testDifferentSearchValueTypes()
|
||||
{
|
||||
$array = [
|
||||
'item1' => [
|
||||
'active' => true,
|
||||
'count' => 5
|
||||
// missing 'label' key
|
||||
],
|
||||
'item2' => [
|
||||
'active' => false,
|
||||
'count' => 10,
|
||||
'label' => 'test'
|
||||
],
|
||||
'item3' => [
|
||||
'active' => true, // same boolean as item1
|
||||
'count' => 15,
|
||||
'label' => 'another'
|
||||
]
|
||||
];
|
||||
|
||||
// Test with boolean
|
||||
$result = ArrayHandler::findArraysMissingKey($array, true, 'label', 'active');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('item1', $result[0]['path']);
|
||||
|
||||
// Test with integer
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 5, 'label', 'count');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('item1', $result[0]['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with empty array
|
||||
*/
|
||||
public function testEmptyArray()
|
||||
{
|
||||
$array = [];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'key');
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with array containing non-array values
|
||||
*/
|
||||
public function testMixedArrayTypes()
|
||||
{
|
||||
$array = [
|
||||
'string_value' => 'hello',
|
||||
'numeric_value' => 123,
|
||||
'array_value' => [
|
||||
'name' => 'test',
|
||||
// missing 'type' key
|
||||
],
|
||||
'another_array' => [
|
||||
'name' => 'test',
|
||||
'type' => 'example'
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($array['array_value'], $result[0]['content']);
|
||||
$this->assertEquals('array_value', $result[0]['path']);
|
||||
$this->assertEquals(['type'], $result[0]['missing_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test path building with deeper nesting
|
||||
*/
|
||||
public function testDeepNestingPathBuilding()
|
||||
{
|
||||
$array = [
|
||||
'level1' => [
|
||||
'level2' => [
|
||||
'level3' => [
|
||||
'items' => [
|
||||
'target_item' => [
|
||||
'name' => 'deep_test',
|
||||
// missing 'required_field'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'deep_test', 'required_field');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('level1:level2:level3:items:target_item', $result[0]['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with custom path separator
|
||||
*/
|
||||
public function testCustomPathSeparator()
|
||||
{
|
||||
$array = [
|
||||
'level1' => [
|
||||
'level2' => [
|
||||
'item' => [
|
||||
'name' => 'test',
|
||||
// missing 'type' key
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type', null, '/');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('level1/level2/item', $result[0]['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test default path separator behavior
|
||||
*/
|
||||
public function testDefaultPathSeparator()
|
||||
{
|
||||
$array = [
|
||||
'parent' => [
|
||||
'child' => [
|
||||
'name' => 'test',
|
||||
// missing 'value' key
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Using default separator (should be ':')
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'value');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('parent:child', $result[0]['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test different path separators don't affect search logic
|
||||
*/
|
||||
public function testPathSeparatorDoesNotAffectSearchLogic()
|
||||
{
|
||||
$array = [
|
||||
'section' => [
|
||||
'data' => [
|
||||
'id' => 123,
|
||||
'name' => 'item'
|
||||
// missing 'status'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Test with different separators - results should be identical except for path
|
||||
$result1 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', ':');
|
||||
$result2 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '.');
|
||||
$result3 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '/');
|
||||
|
||||
$this->assertCount(1, $result1);
|
||||
$this->assertCount(1, $result2);
|
||||
$this->assertCount(1, $result3);
|
||||
|
||||
// Content and missing_key should be the same
|
||||
$this->assertEquals($result1[0]['content'], $result2[0]['content']);
|
||||
$this->assertEquals($result1[0]['content'], $result3[0]['content']);
|
||||
$this->assertEquals($result1[0]['missing_key'], $result2[0]['missing_key']);
|
||||
$this->assertEquals($result1[0]['missing_key'], $result3[0]['missing_key']);
|
||||
|
||||
// Paths should be different based on separator
|
||||
$this->assertEquals('section:data', $result1[0]['path']);
|
||||
$this->assertEquals('section.data', $result2[0]['path']);
|
||||
$this->assertEquals('section/data', $result3[0]['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* test type checking
|
||||
*/
|
||||
public function testStrictTypeChecking()
|
||||
{
|
||||
$array = [
|
||||
'item1' => [
|
||||
'id' => '123', // string
|
||||
'name' => 'test'
|
||||
// missing 'status'
|
||||
],
|
||||
'item2' => [
|
||||
'id' => 123, // integer
|
||||
'name' => 'test2',
|
||||
'status' => 'active'
|
||||
]
|
||||
];
|
||||
|
||||
// Search for integer 123 - should only match item2
|
||||
$result = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id');
|
||||
$this->assertEmpty($result); // item2 has the status key
|
||||
|
||||
// Search for string '123' - should only match item1
|
||||
$result = ArrayHandler::findArraysMissingKey($array, '123', 'status', 'id');
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals('item1', $result[0]['path']);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
// This code was created by Claude Sonnet 4
|
||||
// modification for value checks with assertEqualsCanonicalizing
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
class CoreLibsCombinedArrayHandlerKsortArrayTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test basic ascending sort (default behavior)
|
||||
*/
|
||||
public function testKsortArrayBasicAscending(): void
|
||||
{
|
||||
$input = [
|
||||
'zebra' => 'value1',
|
||||
'apple' => 'value2',
|
||||
'banana' => 'value3',
|
||||
'cherry' => 'value4'
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'apple' => 'value2',
|
||||
'banana' => 'value3',
|
||||
'cherry' => 'value4',
|
||||
'zebra' => 'value1'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test descending sort with reverse=true
|
||||
*/
|
||||
public function testKsortArrayDescending(): void
|
||||
{
|
||||
$input = [
|
||||
'zebra' => 'value1',
|
||||
'apple' => 'value2',
|
||||
'banana' => 'value3',
|
||||
'cherry' => 'value4'
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'zebra' => 'value1',
|
||||
'cherry' => 'value4',
|
||||
'banana' => 'value3',
|
||||
'apple' => 'value2'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input, false, true);
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case-insensitive ascending sort
|
||||
*/
|
||||
public function testKsortArrayCaseInsensitiveAscending(): void
|
||||
{
|
||||
$input = [
|
||||
'Zebra' => 'value1',
|
||||
'apple' => 'value2',
|
||||
'Banana' => 'value3',
|
||||
'cherry' => 'value4'
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'apple' => 'value2',
|
||||
'Banana' => 'value3',
|
||||
'cherry' => 'value4',
|
||||
'Zebra' => 'value1'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input, true);
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case-insensitive descending sort
|
||||
*/
|
||||
public function testKsortArrayCaseInsensitiveDescending(): void
|
||||
{
|
||||
$input = [
|
||||
'Zebra' => 'value1',
|
||||
'apple' => 'value2',
|
||||
'Banana' => 'value3',
|
||||
'cherry' => 'value4'
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'Zebra' => 'value1',
|
||||
'cherry' => 'value4',
|
||||
'Banana' => 'value3',
|
||||
'apple' => 'value2'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input, true, true);
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with mixed case keys to verify case sensitivity behavior
|
||||
*/
|
||||
public function testKsortArrayCaseSensitivityComparison(): void
|
||||
{
|
||||
$input = [
|
||||
'B' => 'value1',
|
||||
'a' => 'value2',
|
||||
'C' => 'value3',
|
||||
'b' => 'value4'
|
||||
];
|
||||
|
||||
// Case-sensitive sort (uppercase comes before lowercase in ASCII)
|
||||
$expectedCaseSensitive = [
|
||||
'B' => 'value1',
|
||||
'C' => 'value3',
|
||||
'a' => 'value2',
|
||||
'b' => 'value4'
|
||||
];
|
||||
|
||||
// Case-insensitive sort
|
||||
$expectedCaseInsensitive = [
|
||||
'a' => 'value2',
|
||||
'B' => 'value1',
|
||||
'b' => 'value4',
|
||||
'C' => 'value3'
|
||||
];
|
||||
|
||||
$resultCaseSensitive = ArrayHandler::ksortArray($input, false);
|
||||
$resultCaseInsensitive = ArrayHandler::ksortArray($input, true);
|
||||
|
||||
$this->assertEquals($expectedCaseSensitive, $resultCaseSensitive);
|
||||
$this->assertEquals($expectedCaseInsensitive, $resultCaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with numeric string keys
|
||||
*/
|
||||
public function testKsortArrayNumericStringKeys(): void
|
||||
{
|
||||
$input = [
|
||||
'10' => 'value1',
|
||||
'2' => 'value2',
|
||||
'1' => 'value3',
|
||||
'20' => 'value4'
|
||||
];
|
||||
|
||||
// String comparison, not numeric
|
||||
$expected = [
|
||||
'1' => 'value3',
|
||||
'10' => 'value1',
|
||||
'2' => 'value2',
|
||||
'20' => 'value4'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with special characters in keys
|
||||
*/
|
||||
public function testKsortArraySpecialCharacters(): void
|
||||
{
|
||||
$input = [
|
||||
'key_with_underscore' => 'value1',
|
||||
'key-with-dash' => 'value2',
|
||||
'key.with.dot' => 'value3',
|
||||
'key with space' => 'value4',
|
||||
'keyWithCamelCase' => 'value5'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
|
||||
// Verify it doesn't throw an error and maintains all keys
|
||||
$this->assertCount(5, $result);
|
||||
$this->assertArrayHasKey('key_with_underscore', $result);
|
||||
$this->assertArrayHasKey('key-with-dash', $result);
|
||||
$this->assertArrayHasKey('key.with.dot', $result);
|
||||
$this->assertArrayHasKey('key with space', $result);
|
||||
$this->assertArrayHasKey('keyWithCamelCase', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with empty array
|
||||
*/
|
||||
public function testKsortArrayEmpty(): void
|
||||
{
|
||||
$input = [];
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
$this->assertEquals([], $result);
|
||||
$this->assertIsArray($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with single element array
|
||||
*/
|
||||
public function testKsortArraySingleElement(): void
|
||||
{
|
||||
$input = ['onlykey' => 'onlyvalue'];
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
$this->assertEquals($input, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that original array is not modified (function returns new array)
|
||||
*/
|
||||
public function testKsortArrayDoesNotModifyOriginal(): void
|
||||
{
|
||||
$original = [
|
||||
'zebra' => 'value1',
|
||||
'apple' => 'value2',
|
||||
'banana' => 'value3'
|
||||
];
|
||||
|
||||
$originalCopy = $original; // Keep a copy for comparison
|
||||
$result = ArrayHandler::ksortArray($original);
|
||||
|
||||
// Original array should remain unchanged
|
||||
$this->assertEquals($originalCopy, $original);
|
||||
$this->assertNotEquals(array_keys($original), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with complex mixed data types as values
|
||||
*/
|
||||
public function testKsortArrayMixedValueTypes(): void
|
||||
{
|
||||
$input = [
|
||||
'string_key' => 'string_value',
|
||||
'array_key' => ['nested', 'array'],
|
||||
'int_key' => 42,
|
||||
'bool_key' => true,
|
||||
'null_key' => null
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input);
|
||||
|
||||
// Check that all keys are preserved and sorted
|
||||
$expectedKeys = ['array_key', 'bool_key', 'int_key', 'null_key', 'string_key'];
|
||||
$this->assertEquals($expectedKeys, array_keys($result));
|
||||
|
||||
// Check that values are preserved correctly
|
||||
$this->assertEquals('string_value', $result['string_key']);
|
||||
$this->assertEquals(['nested', 'array'], $result['array_key']);
|
||||
$this->assertEquals(42, $result['int_key']);
|
||||
$this->assertTrue($result['bool_key']);
|
||||
$this->assertNull($result['null_key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test all parameter combinations
|
||||
*/
|
||||
public function testKsortArrayAllParameterCombinations(): void
|
||||
{
|
||||
$input = [
|
||||
'Delta' => 'value1',
|
||||
'alpha' => 'value2',
|
||||
'Charlie' => 'value3',
|
||||
'bravo' => 'value4'
|
||||
];
|
||||
|
||||
// Test all 4 combinations
|
||||
$result1 = ArrayHandler::ksortArray($input, false, false); // default
|
||||
$result2 = ArrayHandler::ksortArray($input, false, true); // reverse only
|
||||
$result3 = ArrayHandler::ksortArray($input, true, false); // lowercase only
|
||||
$result4 = ArrayHandler::ksortArray($input, true, true); // both
|
||||
|
||||
// Each should produce different ordering
|
||||
$this->assertNotEquals(array_keys($result1), array_keys($result2));
|
||||
$this->assertNotEquals(array_keys($result1), array_keys($result3));
|
||||
$this->assertNotEquals(array_keys($result1), array_keys($result4));
|
||||
$this->assertNotEquals(array_keys($result2), array_keys($result3));
|
||||
$this->assertNotEquals(array_keys($result2), array_keys($result4));
|
||||
$this->assertNotEquals(array_keys($result3), array_keys($result4));
|
||||
|
||||
// But all should have same keys and values, just different order
|
||||
$this->assertEqualsCanonicalizing(array_values($input), array_values($result1));
|
||||
$this->assertEqualsCanonicalizing(array_values($input), array_values($result2));
|
||||
$this->assertEqualsCanonicalizing(array_values($input), array_values($result3));
|
||||
$this->assertEqualsCanonicalizing(array_values($input), array_values($result4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for comprehensive testing
|
||||
*/
|
||||
public function sortingParametersProvider(): array
|
||||
{
|
||||
return [
|
||||
'default' => [false, false],
|
||||
'reverse' => [false, true],
|
||||
'lowercase' => [true, false],
|
||||
'lowercase_reverse' => [true, true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that function works with all parameter combinations using data provider
|
||||
*
|
||||
* @dataProvider sortingParametersProvider
|
||||
*/
|
||||
public function testKsortArrayWithDataProvider(bool $lowerCase, bool $reverse): void
|
||||
{
|
||||
$input = [
|
||||
'Zebra' => 'animal1',
|
||||
'apple' => 'fruit1',
|
||||
'Banana' => 'fruit2',
|
||||
'cat' => 'animal2'
|
||||
];
|
||||
|
||||
$result = ArrayHandler::ksortArray($input, $lowerCase, $reverse);
|
||||
|
||||
// Basic assertions that apply to all combinations
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertArrayHasKey('Zebra', $result);
|
||||
$this->assertArrayHasKey('apple', $result);
|
||||
$this->assertArrayHasKey('Banana', $result);
|
||||
$this->assertArrayHasKey('cat', $result);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
// created by Claude Sonnet 4
|
||||
|
||||
// testRecursiveSearchWithFlatResult had wrong retunr count
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
class CoreLibsCombinedArrayHandlerSelectArrayFromOptionTest extends TestCase
|
||||
{
|
||||
private array $testData;
|
||||
private array $nestedTestData;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->testData = [
|
||||
'item1' => [
|
||||
'name' => 'John',
|
||||
'age' => 25,
|
||||
'status' => 'active',
|
||||
'score' => 85.5
|
||||
],
|
||||
'item2' => [
|
||||
'name' => 'jane',
|
||||
'age' => 30,
|
||||
'status' => 'inactive',
|
||||
'score' => 92.0
|
||||
],
|
||||
'item3' => [
|
||||
'name' => 'Bob',
|
||||
'age' => 25,
|
||||
'status' => 'active',
|
||||
'score' => 78.3
|
||||
],
|
||||
'item4' => [
|
||||
'name' => 'Alice',
|
||||
'age' => 35,
|
||||
'status' => 'pending',
|
||||
'score' => 88.7
|
||||
]
|
||||
];
|
||||
|
||||
$this->nestedTestData = [
|
||||
'level1_a' => [
|
||||
'name' => 'Level1A',
|
||||
'type' => 'parent',
|
||||
'children' => [
|
||||
'child1' => [
|
||||
'name' => 'Child1',
|
||||
'type' => 'child',
|
||||
'active' => true
|
||||
],
|
||||
'child2' => [
|
||||
'name' => 'Child2',
|
||||
'type' => 'child',
|
||||
'active' => false
|
||||
]
|
||||
]
|
||||
],
|
||||
'level1_b' => [
|
||||
'name' => 'Level1B',
|
||||
'type' => 'parent',
|
||||
'children' => [
|
||||
'child3' => [
|
||||
'name' => 'Child3',
|
||||
'type' => 'child',
|
||||
'active' => true,
|
||||
'nested' => [
|
||||
'deep1' => [
|
||||
'name' => 'Deep1',
|
||||
'type' => 'deep',
|
||||
'active' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'item5' => [
|
||||
'name' => 'Direct',
|
||||
'type' => 'child',
|
||||
'active' => false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function testEmptyArrayReturnsEmpty(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption([], 'name', 'John');
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testBasicStringSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'John');
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
$this->assertEquals('John', $result['item1']['name']);
|
||||
}
|
||||
|
||||
public function testBasicIntegerSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'age', 25);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
$this->assertArrayHasKey('item3', $result);
|
||||
}
|
||||
|
||||
public function testBasicFloatSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'score', 85.5);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
$this->assertEquals(85.5, $result['item1']['score']);
|
||||
}
|
||||
|
||||
public function testBasicBooleanSearch(): void
|
||||
{
|
||||
$data = [
|
||||
'item1' => ['enabled' => true, 'name' => 'Test1'],
|
||||
'item2' => ['enabled' => false, 'name' => 'Test2'],
|
||||
'item3' => ['enabled' => true, 'name' => 'Test3']
|
||||
];
|
||||
|
||||
$result = ArrayHandler::selectArrayFromOption($data, 'enabled', true);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
$this->assertArrayHasKey('item3', $result);
|
||||
}
|
||||
|
||||
public function testStrictComparison(): void
|
||||
{
|
||||
$data = [
|
||||
'item1' => ['value' => '25', 'name' => 'String25'],
|
||||
'item2' => ['value' => 25, 'name' => 'Int25'],
|
||||
'item3' => ['value' => 25.0, 'name' => 'Float25']
|
||||
];
|
||||
|
||||
// Non-strict should match all
|
||||
$nonStrictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, false);
|
||||
$this->assertCount(3, $nonStrictResult);
|
||||
|
||||
// Strict should only match exact type
|
||||
$strictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, true);
|
||||
$this->assertCount(1, $strictResult);
|
||||
$this->assertArrayHasKey('item2', $strictResult);
|
||||
}
|
||||
|
||||
public function testCaseInsensitiveSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, true);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('item2', $result);
|
||||
$this->assertEquals('jane', $result['item2']['name']);
|
||||
}
|
||||
|
||||
public function testCaseSensitiveSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, false);
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testRecursiveSearchWithFlatResult(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$this->nestedTestData,
|
||||
'type',
|
||||
'child',
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
':*'
|
||||
);
|
||||
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertArrayHasKey('level1_a:*children:*child1', $result);
|
||||
$this->assertArrayHasKey('level1_a:*children:*child2', $result);
|
||||
$this->assertArrayHasKey('level1_b:*children:*child3', $result);
|
||||
$this->assertArrayHasKey('item5', $result);
|
||||
}
|
||||
|
||||
public function testRecursiveSearchWithNestedResult(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$this->nestedTestData,
|
||||
'type',
|
||||
'child',
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertArrayHasKey('level1_a', $result);
|
||||
$this->assertArrayHasKey('level1_b', $result);
|
||||
$this->assertArrayHasKey('item5', $result);
|
||||
|
||||
// Check nested structure is preserved
|
||||
$this->assertArrayHasKey('children', $result['level1_a']);
|
||||
$this->assertArrayHasKey('child1', $result['level1_a']['children']);
|
||||
$this->assertArrayHasKey('child2', $result['level1_a']['children']);
|
||||
}
|
||||
|
||||
public function testRecursiveSearchDeepNesting(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$this->nestedTestData,
|
||||
'type',
|
||||
'deep',
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
':*'
|
||||
);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('level1_b:*children:*child3:*nested:*deep1', $result);
|
||||
$this->assertEquals('Deep1', $result['level1_b:*children:*child3:*nested:*deep1']['name']);
|
||||
}
|
||||
|
||||
public function testCustomFlatSeparator(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$this->nestedTestData,
|
||||
'type',
|
||||
'child',
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
'|'
|
||||
);
|
||||
|
||||
$this->assertArrayHasKey('level1_a|children|child1', $result);
|
||||
$this->assertArrayHasKey('level1_a|children|child2', $result);
|
||||
$this->assertArrayHasKey('level1_b|children|child3', $result);
|
||||
}
|
||||
|
||||
public function testNonRecursiveSearch(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$this->nestedTestData,
|
||||
'type',
|
||||
'child',
|
||||
false,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
// Should only find direct matches, not nested ones
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('item5', $result);
|
||||
}
|
||||
|
||||
public function testNoMatchesFound(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'NonExistent');
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testMissingLookupKey(): void
|
||||
{
|
||||
$result = ArrayHandler::selectArrayFromOption($this->testData, 'nonexistent_key', 'value');
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testCombinedStrictAndCaseInsensitive(): void
|
||||
{
|
||||
$data = [
|
||||
'item1' => ['name' => 'Test', 'id' => '123'],
|
||||
'item2' => ['name' => 'test', 'id' => 123],
|
||||
'item3' => ['name' => 'TEST', 'id' => '123']
|
||||
];
|
||||
|
||||
// Case insensitive but strict type matching
|
||||
$result = ArrayHandler::selectArrayFromOption($data, 'id', '123', true, true);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
$this->assertArrayHasKey('item3', $result);
|
||||
}
|
||||
|
||||
public function testBooleanWithCaseInsensitive(): void
|
||||
{
|
||||
$data = [
|
||||
'item1' => ['active' => true, 'name' => 'Test1'],
|
||||
'item2' => ['active' => false, 'name' => 'Test2']
|
||||
];
|
||||
|
||||
// Case insensitive flag should not affect boolean comparison
|
||||
$result = ArrayHandler::selectArrayFromOption($data, 'active', true, false, true);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('item1', $result);
|
||||
}
|
||||
|
||||
public function testArrayWithNumericKeys(): void
|
||||
{
|
||||
$data = [
|
||||
0 => ['name' => 'First', 'type' => 'test'],
|
||||
1 => ['name' => 'Second', 'type' => 'test'],
|
||||
2 => ['name' => 'Third', 'type' => 'other']
|
||||
];
|
||||
|
||||
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'test');
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey(0, $result);
|
||||
$this->assertArrayHasKey(1, $result);
|
||||
}
|
||||
|
||||
public function testRecursiveWithMixedKeyTypes(): void
|
||||
{
|
||||
$data = [
|
||||
'string_key' => [
|
||||
'name' => 'Parent',
|
||||
'type' => 'parent',
|
||||
0 => [
|
||||
'name' => 'Child0',
|
||||
'type' => 'child'
|
||||
],
|
||||
'child_key' => [
|
||||
'name' => 'ChildKey',
|
||||
'type' => 'child'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'child', false, false, true, true, ':*');
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('string_key:*0', $result);
|
||||
$this->assertArrayHasKey('string_key:*child_key', $result);
|
||||
}
|
||||
|
||||
public function testAllParametersCombined(): void
|
||||
{
|
||||
$data = [
|
||||
'parent1' => [
|
||||
'name' => 'Parent1',
|
||||
'status' => 'ACTIVE',
|
||||
'children' => [
|
||||
'child1' => [
|
||||
'name' => 'Child1',
|
||||
'status' => 'active'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = ArrayHandler::selectArrayFromOption(
|
||||
$data,
|
||||
'status',
|
||||
'active',
|
||||
false, // not strict
|
||||
true, // case insensitive
|
||||
true, // recursive
|
||||
true, // flat result
|
||||
'|' // custom separator
|
||||
);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('parent1', $result);
|
||||
$this->assertArrayHasKey('parent1|children|child1', $result);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
// This code was created by Claude Sonnet 4
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
class CoreLibsCombinedArrayHandlerSortArrayTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test basic ascending sort without maintaining keys
|
||||
*/
|
||||
public function testBasicAscendingSort()
|
||||
{
|
||||
$input = [3, 1, 4, 1, 5, 9];
|
||||
$expected = [1, 1, 3, 4, 5, 9];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic descending sort without maintaining keys
|
||||
*/
|
||||
public function testBasicDescendingSort()
|
||||
{
|
||||
$input = [3, 1, 4, 1, 5, 9];
|
||||
$expected = [9, 5, 4, 3, 1, 1];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ascending sort with key maintenance
|
||||
*/
|
||||
public function testAscendingSortWithKeyMaintenance()
|
||||
{
|
||||
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
|
||||
$expected = ['a' => 1, 'b' => 1, 'c' => 3, 'd' => 4, 'e' => 5];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, false, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test descending sort with key maintenance
|
||||
*/
|
||||
public function testDescendingSortWithKeyMaintenance()
|
||||
{
|
||||
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
|
||||
$expected = ['e' => 5, 'd' => 4, 'c' => 3, 'a' => 1, 'b' => 1];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, true, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string sorting with lowercase conversion
|
||||
*/
|
||||
public function testStringLowerCaseSort()
|
||||
{
|
||||
$input = ['Banana', 'apple', 'Cherry', 'date'];
|
||||
$expected = ['apple', 'Banana', 'Cherry', 'date'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string sorting with lowercase conversion in reverse
|
||||
*/
|
||||
public function testStringLowerCaseSortReverse()
|
||||
{
|
||||
$input = ['Banana', 'apple', 'Cherry', 'date'];
|
||||
$expected = ['date', 'Cherry', 'Banana', 'apple'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, true, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string sorting with lowercase conversion and key maintenance
|
||||
*/
|
||||
public function testStringLowerCaseSortWithKeys()
|
||||
{
|
||||
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
|
||||
$expected = ['a' => 'apple', 'b' => 'Banana', 'c' => 'Cherry', 'd' => 'date'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, true, false, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test string sorting with lowercase conversion, reverse, and key maintenance
|
||||
*/
|
||||
public function testStringLowerCaseSortReverseWithKeys()
|
||||
{
|
||||
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
|
||||
$expected = ['d' => 'date', 'c' => 'Cherry', 'b' => 'Banana', 'a' => 'apple'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, true, true, true);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test numeric string sorting with SORT_NUMERIC flag
|
||||
*/
|
||||
public function testNumericStringSorting()
|
||||
{
|
||||
$input = ['10', '2', '1', '20'];
|
||||
$expected = ['1', '2', '10', '20'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NUMERIC);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test natural string sorting with SORT_NATURAL flag
|
||||
*/
|
||||
public function testNaturalStringSorting()
|
||||
{
|
||||
$input = ['img1.png', 'img10.png', 'img2.png', 'img20.png'];
|
||||
$expected = ['img1.png', 'img2.png', 'img10.png', 'img20.png'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NATURAL);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with empty array
|
||||
*/
|
||||
public function testEmptyArray()
|
||||
{
|
||||
$input = [];
|
||||
$expected = [];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with single element array
|
||||
*/
|
||||
public function testSingleElementArray()
|
||||
{
|
||||
$input = [42];
|
||||
$expected = [42];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with array containing null values
|
||||
*/
|
||||
public function testArrayWithNullValues()
|
||||
{
|
||||
$input = [3, null, 1, null, 2];
|
||||
$expected = [null, null, 1, 2, 3];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with mixed data types
|
||||
*/
|
||||
public function testMixedDataTypes()
|
||||
{
|
||||
$input = [3, '1', 4.5, '2', 1];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
// Should sort according to PHP's natural comparison rules
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(5, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that original array is not modified (immutability)
|
||||
*/
|
||||
public function testOriginalArrayNotModified()
|
||||
{
|
||||
$original = [3, 1, 4, 1, 5, 9];
|
||||
$input = $original;
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($original, $input);
|
||||
$this->assertNotEquals($input, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case sensitivity without lowercase flag
|
||||
*/
|
||||
public function testCaseSensitivityWithoutLowercase()
|
||||
{
|
||||
$input = ['Banana', 'apple', 'Cherry'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
// Capital letters should come before lowercase in ASCII sort
|
||||
$this->assertEquals('Banana', $result[0]);
|
||||
$this->assertEquals('Cherry', $result[1]);
|
||||
$this->assertEquals('apple', $result[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test all parameters combination
|
||||
*/
|
||||
public function testAllParametersCombination()
|
||||
{
|
||||
$input = ['z' => 'Zebra', 'a' => 'apple', 'b' => 'Banana'];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, true, true, true, SORT_REGULAR);
|
||||
|
||||
// Should be sorted by lowercase, reversed, with keys maintained
|
||||
$keys = array_keys($result);
|
||||
$values = array_values($result);
|
||||
|
||||
$this->assertEquals(['z', 'b', 'a'], $keys);
|
||||
$this->assertEquals(['Zebra', 'Banana', 'apple'], $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test floating point numbers
|
||||
*/
|
||||
public function testFloatingPointNumbers()
|
||||
{
|
||||
$input = [3.14, 2.71, 1.41, 1.73];
|
||||
$expected = [1.41, 1.73, 2.71, 3.14];
|
||||
|
||||
$result = ArrayHandler::sortArray($input);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with duplicate values and key maintenance
|
||||
*/
|
||||
public function testDuplicateValuesWithKeyMaintenance()
|
||||
{
|
||||
$input = ['first' => 1, 'second' => 2, 'third' => 1, 'fourth' => 2];
|
||||
|
||||
$result = ArrayHandler::sortArray($input, false, false, true);
|
||||
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertEquals([1, 1, 2, 2], array_values($result));
|
||||
// Keys should be preserved
|
||||
$this->assertArrayHasKey('first', $result);
|
||||
$this->assertArrayHasKey('second', $result);
|
||||
$this->assertArrayHasKey('third', $result);
|
||||
$this->assertArrayHasKey('fourth', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for comprehensive parameter testing
|
||||
*/
|
||||
public function sortParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
'basic_ascending' => [
|
||||
[3, 1, 4, 2],
|
||||
false, false, false, SORT_REGULAR,
|
||||
[1, 2, 3, 4]
|
||||
],
|
||||
'basic_descending' => [
|
||||
[3, 1, 4, 2],
|
||||
false, true, false, SORT_REGULAR,
|
||||
[4, 3, 2, 1]
|
||||
],
|
||||
'lowercase_ascending' => [
|
||||
['Banana', 'apple', 'Cherry'],
|
||||
true, false, false, SORT_REGULAR,
|
||||
['apple', 'Banana', 'Cherry']
|
||||
],
|
||||
'lowercase_descending' => [
|
||||
['Banana', 'apple', 'Cherry'],
|
||||
true, true, false, SORT_REGULAR,
|
||||
['Cherry', 'Banana', 'apple']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test various parameter combinations using data provider
|
||||
*
|
||||
* @dataProvider sortParameterProvider
|
||||
*/
|
||||
public function testSortParameterCombinations(
|
||||
array $input,
|
||||
bool $lowercase,
|
||||
bool $reverse,
|
||||
bool $maintainKeys,
|
||||
int $params,
|
||||
array $expected
|
||||
) {
|
||||
$result = ArrayHandler::sortArray($input, $lowercase, $reverse, $maintainKeys, $params);
|
||||
|
||||
if (!$maintainKeys) {
|
||||
$this->assertEquals($expected, $result);
|
||||
} else {
|
||||
$this->assertEquals($expected, array_values($result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
File diff suppressed because it is too large
Load Diff
283
4dev/tests/Convert/CoreLibsConvertStringsRegexValidateTest.php
Normal file
283
4dev/tests/Convert/CoreLibsConvertStringsRegexValidateTest.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
// This code was created by Claude Sonnet 4
|
||||
// FIX:
|
||||
// '/test{/', // Unmatched brace -> this is valid
|
||||
// '/test{1,}/', // Invalid quantifier -> this is valid
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Convert\Strings;
|
||||
|
||||
/**
|
||||
* Test class for CoreLibs\Convert\Strings regex validation methods
|
||||
*/
|
||||
class CoreLibsConvertStringsRegexValidateTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test isValidRegex with valid regex patterns
|
||||
*/
|
||||
public function testIsValidRegexWithValidPatterns(): void
|
||||
{
|
||||
$validPatterns = [
|
||||
'/^[a-zA-Z0-9]+$/',
|
||||
'/test/',
|
||||
'/\d+/',
|
||||
'/^hello.*world$/',
|
||||
'/[0-9]{3}-[0-9]{3}-[0-9]{4}/',
|
||||
'#^https?://.*#i',
|
||||
'~^[a-z]+~',
|
||||
'|test|',
|
||||
'/^$/m',
|
||||
'/\w+/u',
|
||||
];
|
||||
|
||||
foreach ($validPatterns as $pattern) {
|
||||
$this->assertTrue(
|
||||
Strings::isValidRegex($pattern),
|
||||
"Pattern '{$pattern}' should be valid"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test isValidRegex with invalid regex patterns
|
||||
*/
|
||||
public function testIsValidRegexWithInvalidPatterns(): void
|
||||
{
|
||||
$invalidPatterns = [
|
||||
'/[/', // Unmatched bracket
|
||||
'/test[/', // Unmatched bracket
|
||||
'/(?P<name>/', // Unmatched parenthesis
|
||||
'/(?P<>test)/', // Invalid named group
|
||||
'/test\\/', // Invalid escape at end
|
||||
'/(test/', // Unmatched parenthesis
|
||||
'/test)/', // Unmatched parenthesis
|
||||
// '/test{/', // Unmatched brace -> this is valid
|
||||
// '/test{1,}/', // Invalid quantifier -> this is valid
|
||||
'/[z-a]/', // Invalid character range
|
||||
'invalid', // No delimiters
|
||||
'', // Empty string
|
||||
'/(?P<123>test)/', // Invalid named group name
|
||||
];
|
||||
|
||||
foreach ($invalidPatterns as $pattern) {
|
||||
$this->assertFalse(
|
||||
Strings::isValidRegex($pattern),
|
||||
"Pattern '{$pattern}' should be invalid"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getLastRegexErrorString returns correct error messages
|
||||
*/
|
||||
public function testGetLastRegexErrorStringReturnsCorrectMessages(): void
|
||||
{
|
||||
// Test with a valid regex first to ensure clean state
|
||||
Strings::isValidRegex('/valid/');
|
||||
$this->assertEquals('No error', Strings::getLastRegexErrorString());
|
||||
|
||||
// Test with invalid regex to trigger an error
|
||||
Strings::isValidRegex('/[/');
|
||||
$errorMessage = Strings::getLastRegexErrorString();
|
||||
|
||||
// The error message should be one of the defined messages
|
||||
$this->assertContains($errorMessage, array_values(Strings::PREG_ERROR_MESSAGES));
|
||||
$this->assertNotEquals('Unknown error', $errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getLastRegexErrorString with unknown error
|
||||
*/
|
||||
public function testGetLastRegexErrorStringWithUnknownError(): void
|
||||
{
|
||||
// This is harder to test directly since we can't easily mock preg_last_error()
|
||||
// but we can test the fallback behavior by reflection or assume it works
|
||||
|
||||
// At minimum, ensure it returns a string
|
||||
$result = Strings::getLastRegexErrorString();
|
||||
$this->assertIsString($result);
|
||||
$this->assertNotEmpty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validateRegex with valid patterns
|
||||
*/
|
||||
public function testValidateRegexWithValidPatterns(): void
|
||||
{
|
||||
$validPatterns = [
|
||||
'/^test$/',
|
||||
'/\d+/',
|
||||
'/[a-z]+/i',
|
||||
];
|
||||
|
||||
foreach ($validPatterns as $pattern) {
|
||||
$result = Strings::validateRegex($pattern);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertArrayHasKey('valid', $result);
|
||||
$this->assertArrayHasKey('preg_error', $result);
|
||||
$this->assertArrayHasKey('error', $result);
|
||||
|
||||
$this->assertTrue($result['valid'], "Pattern '{$pattern}' should be valid");
|
||||
$this->assertEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||
$this->assertNull($result['error']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validateRegex with invalid patterns
|
||||
*/
|
||||
public function testValidateRegexWithInvalidPatterns(): void
|
||||
{
|
||||
$invalidPatterns = [
|
||||
'/[/', // Unmatched bracket
|
||||
'/(?P<name>/', // Unmatched parenthesis
|
||||
'/test\\/', // Invalid escape at end
|
||||
'/(test/', // Unmatched parenthesis
|
||||
];
|
||||
|
||||
foreach ($invalidPatterns as $pattern) {
|
||||
$result = Strings::validateRegex($pattern);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertArrayHasKey('valid', $result);
|
||||
$this->assertArrayHasKey('preg_error', $result);
|
||||
$this->assertArrayHasKey('error', $result);
|
||||
$this->assertArrayHasKey('pcre_error', $result);
|
||||
|
||||
$this->assertFalse($result['valid'], "Pattern '{$pattern}' should be invalid");
|
||||
$this->assertNotEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||
$this->assertIsString($result['error']);
|
||||
$this->assertNotNull($result['error']);
|
||||
$this->assertNotEmpty($result['error']);
|
||||
|
||||
// Verify error message is from our defined messages or 'Unknown error'
|
||||
$this->assertTrue(
|
||||
in_array($result['error'], array_values(Strings::PREG_ERROR_MESSAGES)) ||
|
||||
$result['error'] === 'Unknown error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validateRegex array structure
|
||||
*/
|
||||
public function testValidateRegexArrayStructure(): void
|
||||
{
|
||||
$result = Strings::validateRegex('/test/');
|
||||
|
||||
// Test array structure for valid regex
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertArrayHasKey('valid', $result);
|
||||
$this->assertArrayHasKey('preg_error', $result);
|
||||
$this->assertArrayHasKey('error', $result);
|
||||
|
||||
$result = Strings::validateRegex('/[/');
|
||||
|
||||
// Test array structure for invalid regex
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertArrayHasKey('valid', $result);
|
||||
$this->assertArrayHasKey('preg_error', $result);
|
||||
$this->assertArrayHasKey('error', $result);
|
||||
$this->assertArrayHasKey('pcre_error', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that methods handle edge cases properly
|
||||
*/
|
||||
public function testEdgeCases(): void
|
||||
{
|
||||
// Empty string
|
||||
$this->assertFalse(Strings::isValidRegex(''));
|
||||
|
||||
$result = Strings::validateRegex('');
|
||||
$this->assertFalse($result['valid']);
|
||||
|
||||
// Very long pattern
|
||||
$longPattern = '/' . str_repeat('a', 1000) . '/';
|
||||
$this->assertTrue(Strings::isValidRegex($longPattern));
|
||||
|
||||
// Unicode patterns
|
||||
$this->assertTrue(Strings::isValidRegex('/\p{L}+/u'));
|
||||
$this->assertTrue(Strings::isValidRegex('/[α-ω]+/u'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test PREG_ERROR_MESSAGES constant accessibility
|
||||
*/
|
||||
public function testPregErrorMessagesConstant(): void
|
||||
{
|
||||
$this->assertIsArray(Strings::PREG_ERROR_MESSAGES);
|
||||
$this->assertNotEmpty(Strings::PREG_ERROR_MESSAGES);
|
||||
|
||||
// Check that all expected PREG constants are defined
|
||||
$expectedKeys = [
|
||||
PREG_NO_ERROR,
|
||||
PREG_INTERNAL_ERROR,
|
||||
PREG_BACKTRACK_LIMIT_ERROR,
|
||||
PREG_RECURSION_LIMIT_ERROR,
|
||||
PREG_BAD_UTF8_ERROR,
|
||||
PREG_BAD_UTF8_OFFSET_ERROR,
|
||||
PREG_JIT_STACKLIMIT_ERROR,
|
||||
];
|
||||
|
||||
foreach ($expectedKeys as $key) {
|
||||
$this->assertArrayHasKey($key, Strings::PREG_ERROR_MESSAGES);
|
||||
$this->assertIsString(Strings::PREG_ERROR_MESSAGES[$key]);
|
||||
$this->assertNotEmpty(Strings::PREG_ERROR_MESSAGES[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error state isolation between method calls
|
||||
*/
|
||||
public function testErrorStateIsolation(): void
|
||||
{
|
||||
// Start with invalid regex
|
||||
Strings::isValidRegex('/[/');
|
||||
$firstError = Strings::getLastRegexErrorString();
|
||||
$this->assertNotEquals('No error', $firstError);
|
||||
|
||||
// Use valid regex
|
||||
Strings::isValidRegex('/valid/');
|
||||
$secondError = Strings::getLastRegexErrorString();
|
||||
$this->assertEquals('No error', $secondError);
|
||||
|
||||
// Verify validateRegex clears previous errors
|
||||
$result = Strings::validateRegex('/valid/');
|
||||
$this->assertTrue($result['valid']);
|
||||
$this->assertEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test various regex delimiters
|
||||
*/
|
||||
public function testDifferentDelimiters(): void
|
||||
{
|
||||
$patterns = [
|
||||
'/test/', // forward slash
|
||||
'#test#', // hash
|
||||
'~test~', // tilde
|
||||
'|test|', // pipe
|
||||
'@test@', // at symbol
|
||||
'!test!', // exclamation
|
||||
'%test%', // percent
|
||||
];
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
$this->assertTrue(
|
||||
Strings::isValidRegex($pattern),
|
||||
"Pattern with delimiter '{$pattern}' should be valid"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -24,117 +24,83 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
{
|
||||
// 0: input
|
||||
// 1: format
|
||||
// 2: split characters as string, null for default
|
||||
// 3: expected
|
||||
return [
|
||||
'all empty string' => [
|
||||
'',
|
||||
'',
|
||||
null,
|
||||
''
|
||||
],
|
||||
'empty input string' => [
|
||||
'',
|
||||
'2-2',
|
||||
null,
|
||||
''
|
||||
],
|
||||
'empty format string string' => [
|
||||
'1234',
|
||||
'',
|
||||
null,
|
||||
'1234'
|
||||
],
|
||||
'string format match' => [
|
||||
'1234',
|
||||
'2-2',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format trailing match' => [
|
||||
'1234',
|
||||
'2-2-',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format leading match' => [
|
||||
'1234',
|
||||
'-2-2',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format double inside match' => [
|
||||
'1234',
|
||||
'2--2',
|
||||
null,
|
||||
'12--34',
|
||||
],
|
||||
'string format short first' => [
|
||||
'1',
|
||||
'2-2',
|
||||
null,
|
||||
'1'
|
||||
],
|
||||
'string format match first' => [
|
||||
'12',
|
||||
'2-2',
|
||||
null,
|
||||
'12'
|
||||
],
|
||||
'string format short second' => [
|
||||
'123',
|
||||
'2-2',
|
||||
null,
|
||||
'12-3'
|
||||
],
|
||||
'string format too long' => [
|
||||
'1234567',
|
||||
'2-2',
|
||||
null,
|
||||
'12-34-567'
|
||||
],
|
||||
'string format invalid format string' => [
|
||||
'1234',
|
||||
'2_2',
|
||||
null,
|
||||
'1234'
|
||||
],
|
||||
'different split character' => [
|
||||
'1234',
|
||||
'2_2',
|
||||
'_',
|
||||
'12_34'
|
||||
],
|
||||
'mixed split characters' => [
|
||||
'123456',
|
||||
'2-2_2',
|
||||
'-_',
|
||||
'12-34_56'
|
||||
],
|
||||
'length mixed' => [
|
||||
'ABCD12345568ABC13',
|
||||
'2-4_5-2#4',
|
||||
'-_#',
|
||||
'AB-CD12_34556-8A#BC13'
|
||||
],
|
||||
'split with split chars in string' => [
|
||||
'12-34',
|
||||
'2-2',
|
||||
null,
|
||||
'12--3-4'
|
||||
],
|
||||
'mutltibyte string' => [
|
||||
'あいうえ',
|
||||
'2-2',
|
||||
null,
|
||||
'あいうえ'
|
||||
],
|
||||
'mutltibyte split string' => [
|
||||
'1234',
|
||||
'2-2',
|
||||
null,
|
||||
'1234'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -143,29 +109,137 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
*
|
||||
* @covers ::splitFormatString
|
||||
* @dataProvider splitFormatStringProvider
|
||||
* @testdox splitFormatString $input with format $format and splitters $split_characters will be $expected [$_dataName]
|
||||
* @testdox splitFormatString $input with format $format will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $format
|
||||
* @param string|null $split_characters
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testSplitFormatString(
|
||||
string $input,
|
||||
string $format,
|
||||
string $expected
|
||||
): void {
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||
$input,
|
||||
$format,
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
/** check exceptions */
|
||||
public function splitFormatStringExceptionProvider(): array
|
||||
{
|
||||
return [
|
||||
'string format with no splitter match' => [
|
||||
'1234',
|
||||
'22',
|
||||
'12-34'
|
||||
],
|
||||
'invalid format string' => [
|
||||
'1234',
|
||||
'2あ2',
|
||||
],
|
||||
'mutltibyte string' => [
|
||||
'あいうえ',
|
||||
'2-2',
|
||||
],
|
||||
'mutltibyte split string' => [
|
||||
'1234',
|
||||
'2-2',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::splitFormatStringFixed
|
||||
* @dataProvider splitFormatStringExceptionProvider
|
||||
* @testdox splitFormatString Exception catch checks for $input with $format[$_dataName]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSplitFormatStringExceptions(string $input, string $format): void
|
||||
{
|
||||
// catch exception
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
\CoreLibs\Convert\Strings::splitFormatString($input, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* test for split Format string fixed length
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function splitFormatStringFixedProvider(): array
|
||||
{
|
||||
return [
|
||||
'normal split, default split char' => [
|
||||
'abcdefg',
|
||||
4,
|
||||
null,
|
||||
'abcd-efg'
|
||||
],
|
||||
'noraml split, other single split char' => [
|
||||
'abcdefg',
|
||||
4,
|
||||
"=",
|
||||
'abcd=efg'
|
||||
],
|
||||
'noraml split, other multiple split char' => [
|
||||
'abcdefg',
|
||||
4,
|
||||
"-=-",
|
||||
'abcd-=-efg'
|
||||
],
|
||||
'non ascii characters' => [
|
||||
'あいうえお',
|
||||
2,
|
||||
"-",
|
||||
'あい-うえ-お'
|
||||
],
|
||||
'empty string' => [
|
||||
'',
|
||||
4,
|
||||
"-",
|
||||
''
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::splitFormatStringFixed
|
||||
* @dataProvider splitFormatStringFixedProvider
|
||||
* @testdox splitFormatStringFixed $input with length $split_length and split chars $split_characters will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param int $split_length
|
||||
* @param string|null $split_characters
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testSplitFormatStringFixed(
|
||||
string $input,
|
||||
int $split_length,
|
||||
?string $split_characters,
|
||||
string $expected
|
||||
): void {
|
||||
if ($split_characters === null) {
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||
$input,
|
||||
$format
|
||||
$split_length
|
||||
);
|
||||
} else {
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||
$input,
|
||||
$format,
|
||||
$split_length,
|
||||
$split_characters
|
||||
);
|
||||
}
|
||||
@@ -175,6 +249,36 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function splitFormatStringFixedExceptionProvider(): array
|
||||
{
|
||||
return [
|
||||
'split length too short' => [
|
||||
'abcdefg',
|
||||
-1,
|
||||
],
|
||||
'split length longer than string' => [
|
||||
'abcdefg',
|
||||
20,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::splitFormatStringFixed
|
||||
* @dataProvider splitFormatStringFixedExceptionProvider
|
||||
* @testdox splitFormatStringFixed Exception catch checks for $input with $length [$_dataName]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSplitFormatStringFixedExceptions(string $input, int $length): void
|
||||
{
|
||||
// catch exception
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
\CoreLibs\Convert\Strings::splitFormatStringFixed($input, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -378,6 +482,222 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
\CoreLibs\Convert\Strings::stripUTF8BomBytes($file)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function allCharsInSetProvider(): array
|
||||
{
|
||||
return [
|
||||
'find' => [
|
||||
'abc',
|
||||
'abcdef',
|
||||
true
|
||||
],
|
||||
'not found' => [
|
||||
'abcz',
|
||||
'abcdef',
|
||||
false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::allCharsInSet
|
||||
* @dataProvider allCharsInSetProvider
|
||||
* @testdox allCharsInSet $input in $haystack with expected $expected [$_dataName]
|
||||
*
|
||||
* @param string $needle
|
||||
* @param string $haystack
|
||||
* @param bool $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testAllCharsInSet(string $needle, string $haystack, bool $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Strings::allCharsInSet($needle, $haystack)
|
||||
);
|
||||
}
|
||||
|
||||
public function buildCharStringFromListsProvider(): array
|
||||
{
|
||||
return [
|
||||
'test a' => [
|
||||
'abc',
|
||||
['a', 'b', 'c'],
|
||||
],
|
||||
'test b' => [
|
||||
'abc123',
|
||||
['a', 'b', 'c'],
|
||||
['1', '2', '3'],
|
||||
],
|
||||
'test c: no params' => [
|
||||
'',
|
||||
],
|
||||
'test c: empty 1' => [
|
||||
'',
|
||||
[]
|
||||
],
|
||||
'test nested' => [
|
||||
'abc',
|
||||
[['a'], ['b'], ['c']],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::buildCharStringFromLists
|
||||
* @dataProvider buildCharStringFromListsProvider
|
||||
* @testdox buildCharStringFromLists all $input convert to $expected [$_dataName]
|
||||
*
|
||||
* @param string $expected
|
||||
* @param array ...$input
|
||||
* @return void
|
||||
*/
|
||||
public function testBuildCharStringFromLists(string $expected, array ...$input): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Strings::buildCharStringFromLists(...$input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function removeDuplicatesProvider(): array
|
||||
{
|
||||
return [
|
||||
'test no change' => [
|
||||
'ABCDEFG',
|
||||
'ABCDEFG',
|
||||
],
|
||||
'test simple' => [
|
||||
'aa',
|
||||
'a'
|
||||
],
|
||||
'test keep lower and uppwer case' => [
|
||||
'AaBbCc',
|
||||
'AaBbCc'
|
||||
],
|
||||
'test unqiue' => [
|
||||
'aabbcc',
|
||||
'abc'
|
||||
],
|
||||
'test multibyte no change' => [
|
||||
'あいうえお',
|
||||
'あいうえお',
|
||||
],
|
||||
'test multibyte' => [
|
||||
'ああいいううええおお',
|
||||
'あいうえお',
|
||||
],
|
||||
'test multibyte special' => [
|
||||
'あぁいぃうぅえぇおぉ',
|
||||
'あぁいぃうぅえぇおぉ',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::removeDuplicates
|
||||
* @dataProvider removeDuplicatesProvider
|
||||
* @testdox removeDuplicates make $input unqiue to $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testRemoveDuplicates(string $input, string $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Strings::removeDuplicates($input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function isValidRegexSimpleProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid regex' => [
|
||||
'/^[A-z]$/',
|
||||
true,
|
||||
[
|
||||
'valid' => true,
|
||||
'preg_error' => 0,
|
||||
'error' => null,
|
||||
'pcre_error' => null
|
||||
],
|
||||
],
|
||||
'invalid regex A' => [
|
||||
'/^[A-z]$',
|
||||
false,
|
||||
[
|
||||
'valid' => false,
|
||||
'preg_error' => 1,
|
||||
'error' => 'Internal PCRE error',
|
||||
'pcre_error' => 'Internal error'
|
||||
],
|
||||
],
|
||||
'invalid regex B' => [
|
||||
'/^[A-z$',
|
||||
false,
|
||||
[
|
||||
'valid' => false,
|
||||
'preg_error' => 1,
|
||||
'error' => 'Internal PCRE error',
|
||||
'pcre_error' => 'Internal error'
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::isValidRegexSimple
|
||||
* @dataProvider isValidRegexSimpleProvider
|
||||
* @testdox isValidRegexSimple make $input unqiue to $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param bool $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testIsValidRegexSimple(string $input, bool $expected, array $expected_extended): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Strings::isValidRegex($input),
|
||||
'Regex is not valid'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_extended,
|
||||
\CoreLibs\Convert\Strings::validateRegex($input),
|
||||
'Validation of regex failed'
|
||||
);
|
||||
$this->assertEquals(
|
||||
// for true null is set, so we get here No Error
|
||||
$expected_extended['error'] ?? \CoreLibs\Convert\Strings::PREG_ERROR_MESSAGES[0],
|
||||
\CoreLibs\Convert\Strings::getLastRegexErrorString(),
|
||||
'Cannot match last preg error string'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -13,32 +13,6 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keyLenghtProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid key length' => [
|
||||
0 => 6,
|
||||
1 => true,
|
||||
2 => 6,
|
||||
],
|
||||
'negative key length' => [
|
||||
0 => -1,
|
||||
1 => false,
|
||||
2 => 4,
|
||||
],
|
||||
'tpp big key length' => [
|
||||
0 => 300,
|
||||
1 => false,
|
||||
2 => 4,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -47,109 +21,67 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
public function randomKeyGenProvider(): array
|
||||
{
|
||||
return [
|
||||
'default key length' => [
|
||||
// just key length
|
||||
'default key length, default char set' => [
|
||||
0 => null,
|
||||
1 => 4
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT
|
||||
],
|
||||
'set -1 key length default' => [
|
||||
'set -1 key length, default char set' => [
|
||||
0 => -1,
|
||||
1 => 4,
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
],
|
||||
'set too large key length' => [
|
||||
'set 0 key length, default char set' => [
|
||||
0 => -1,
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
],
|
||||
'set too large key length, default char set' => [
|
||||
0 => 300,
|
||||
1 => 4,
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
],
|
||||
'set override key lenght' => [
|
||||
'set override key lenght, default char set' => [
|
||||
0 => 6,
|
||||
1 => 6,
|
||||
],
|
||||
// just character set
|
||||
'default key length, different char set A' => [
|
||||
0 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
2 => [
|
||||
'A', 'B', 'C'
|
||||
],
|
||||
],
|
||||
'different key length, different char set B' => [
|
||||
0 => 16,
|
||||
1 => 16,
|
||||
2 => [
|
||||
'A', 'B', 'C'
|
||||
],
|
||||
3 => [
|
||||
'1', '2', '3'
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Alternative more efficient version using strpos
|
||||
/**
|
||||
* 1
|
||||
* check if all characters are in set
|
||||
*
|
||||
* @return array
|
||||
* @param string $input
|
||||
* @param string $allowed_chars
|
||||
* @return bool
|
||||
*/
|
||||
public function keepKeyLengthProvider(): array
|
||||
private function allCharsInSet(string $input, string $allowed_chars): bool
|
||||
{
|
||||
return [
|
||||
'set too large' => [
|
||||
0 => 6,
|
||||
1 => 300,
|
||||
2 => 6,
|
||||
],
|
||||
'set too small' => [
|
||||
0 => 8,
|
||||
1 => -2,
|
||||
2 => 8,
|
||||
],
|
||||
'change valid' => [
|
||||
0 => 10,
|
||||
1 => 6,
|
||||
2 => 6,
|
||||
]
|
||||
];
|
||||
}
|
||||
$inputLength = strlen($input);
|
||||
|
||||
/**
|
||||
* run before each test and reset to default 4
|
||||
*
|
||||
* @before
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetKeyLength(): void
|
||||
{
|
||||
\CoreLibs\Create\RandomKey::setRandomKeyLength(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* check that first length is 4
|
||||
*
|
||||
* @covers ::getRandomKeyLength
|
||||
* @testWith [4]
|
||||
* @testdox getRandomKeyLength on init will be $expected [$_dataName]
|
||||
*
|
||||
* @param integer $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testGetRandomKeyLengthInit(int $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setRandomKeyLength
|
||||
* @covers ::getRandomKeyLength
|
||||
* @dataProvider keyLenghtProvider
|
||||
* @testdox setRandomKeyLength $input will be $expected, compare to $compare [$_dataName]
|
||||
*
|
||||
* @param integer $input
|
||||
* @param boolean $expected
|
||||
* @param integer $compare
|
||||
* @return void
|
||||
*/
|
||||
public function testSetRandomKeyLength(int $input, bool $expected, int $compare): void
|
||||
{
|
||||
// set
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input)
|
||||
);
|
||||
// read test, if false, use compare check
|
||||
if ($expected === false) {
|
||||
$input = $compare;
|
||||
for ($i = 0; $i < $inputLength; $i++) {
|
||||
if (strpos($allowed_chars, $input[$i]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->assertEquals(
|
||||
$input,
|
||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,43 +95,41 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
* @param integer $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testRandomKeyGen(?int $input, int $expected): void
|
||||
public function testRandomKeyGen(?int $input, int $expected, array ...$key_range): void
|
||||
{
|
||||
$__key_data = \CoreLibs\Create\RandomKey::KEY_CHARACTER_RANGE_DEFAULT;
|
||||
if (count($key_range)) {
|
||||
$__key_data = join('', array_unique(array_merge(...$key_range)));
|
||||
}
|
||||
if ($input === null) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
strlen(\CoreLibs\Create\RandomKey::randomKeyGen())
|
||||
);
|
||||
} else {
|
||||
} elseif ($input !== null && !count($key_range)) {
|
||||
$random_key = \CoreLibs\Create\RandomKey::randomKeyGen($input);
|
||||
$this->assertTrue(
|
||||
$this->allCharsInSet($random_key, $__key_data),
|
||||
'Characters not valid'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
strlen(\CoreLibs\Create\RandomKey::randomKeyGen($input))
|
||||
strlen($random_key),
|
||||
'String length not matching'
|
||||
);
|
||||
} elseif (count($key_range)) {
|
||||
$random_key = \CoreLibs\Create\RandomKey::randomKeyGen($input, ...$key_range);
|
||||
$this->assertTrue(
|
||||
$this->allCharsInSet($random_key, $__key_data),
|
||||
'Characters not valid'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
strlen($random_key),
|
||||
'String length not matching'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that if set to n and then invalid, it keeps the previous one
|
||||
* or if second change valid, second will be shown
|
||||
*
|
||||
* @covers ::setRandomKeyLength
|
||||
* @dataProvider keepKeyLengthProvider
|
||||
* @testdox keep setRandomKeyLength set with $input_valid and then $input_invalid will be $expected [$_dataName]
|
||||
*
|
||||
* @param integer $input_valid
|
||||
* @param integer $input_invalid
|
||||
* @param integer $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testKeepKeyLength(int $input_valid, int $input_invalid, int $expected): void
|
||||
{
|
||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input_valid);
|
||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input_invalid);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -249,7 +249,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$this->assertFalse(
|
||||
$log->loggingLevelIsDebug()
|
||||
);
|
||||
// not set, should be debug]
|
||||
// not set, should be debug
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetLoggingLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
@@ -297,6 +297,71 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$log->setLoggingLevel('NotGood');
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setErrorLogWriteLevel
|
||||
* @covers ::getErrorLogWriteLevel
|
||||
* @testdox setErrorLogWriteLevel set/get checks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSetErrorLogWriteLevel(): void
|
||||
{
|
||||
// valid that is not Debug
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
'error_log_write_level' => Level::Error
|
||||
]);
|
||||
$this->assertEquals(
|
||||
Level::Error,
|
||||
$log->getErrorLogWriteLevel()
|
||||
);
|
||||
// not set on init
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
]);
|
||||
$this->assertEquals(
|
||||
Level::Emergency,
|
||||
$log->getErrorLogWriteLevel()
|
||||
);
|
||||
// invalid, should be Emergency, will throw excpetion too
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Option: "error_log_write_level" is not of instance \CoreLibs\Logging\Logger\Level'
|
||||
);
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetLoggingLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
'error_log_write_level' => 'I'
|
||||
]);
|
||||
$this->assertEquals(
|
||||
Level::Emergency,
|
||||
$log->getErrorLogWriteLevel()
|
||||
);
|
||||
// set valid then change
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
'error_log_write_level' => Level::Error
|
||||
]);
|
||||
$this->assertEquals(
|
||||
Level::Error,
|
||||
$log->getErrorLogWriteLevel()
|
||||
);
|
||||
$log->setErrorLogWriteLevel(Level::Notice);
|
||||
$this->assertEquals(
|
||||
Level::Notice,
|
||||
$log->getErrorLogWriteLevel()
|
||||
);
|
||||
// illegal logging level
|
||||
$this->expectException(\Psr\Log\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches("/^Level \"NotGood\" is not defined, use one of: /");
|
||||
$log->setErrorLogWriteLevel('NotGood');
|
||||
}
|
||||
|
||||
// setLogFileId
|
||||
// getLogFileId
|
||||
|
||||
|
||||
Reference in New Issue
Block a user