Skip to content

Commit 3a895da

Browse files
committed
First Util tests;
Changed Util::escapeString() to only escape non-alphanumeric characters; Renamed StateAlteringFeaturesTest.php to ClientStateAlteringFeaturesTest.php.
1 parent 8a4cfea commit 3a895da

File tree

4 files changed

+188
-26
lines changed

4 files changed

+188
-26
lines changed

src/PEAR2/Net/RouterOS/Util.php

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
*/
2121
namespace PEAR2\Net\RouterOS;
2222

23+
/**
24+
* Used as a holder for the entry cache.
25+
*/
26+
use SplFixedArray;
27+
2328
/**
2429
* Utility class.
2530
*
@@ -62,16 +67,31 @@ class Util
6267
*/
6368
public static function escapeString($value)
6469
{
65-
$result = '';
66-
for ($i = 0, $l = strlen($value); $i < $l; ++$i) {
67-
$result .= '\\' . str_pad(
68-
strtoupper(dechex(ord($value[$i]))),
69-
2,
70-
'0',
71-
STR_PAD_LEFT
72-
);
73-
}
74-
return $result;
70+
return preg_replace_callback(
71+
'/[^A-Za-z0-9]/S',
72+
array(self, '_escapeCharacter'),
73+
$value
74+
);
75+
}
76+
77+
/**
78+
* Escapes a character for a RouterOS scripting context.
79+
*
80+
* Escapes a character for a RouterOS scripting context. Intended to only be
81+
* called for non-alphanumeric characters.
82+
*
83+
* @param string $char The character to be escaped.
84+
*
85+
* @return string The escaped character.
86+
*/
87+
private static function _escapeCharacter($char)
88+
{
89+
return '\\' . str_pad(
90+
strtoupper(dechex(ord($char))),
91+
2,
92+
'0',
93+
STR_PAD_LEFT
94+
);
7595
}
7696

7797
/**
@@ -202,7 +222,7 @@ function ($value) use (&$idList) {
202222
* Gets a value of a specified entry at the current menu.
203223
*
204224
* @param int $number A number identifying the entry you're
205-
* targeting. Can also be a name.
225+
* targeting. Can also be a name or ID.
206226
* @param string $value_name The name of the value you want to get.
207227
*
208228
* @return string|null|bool The value of the specified property. If the
@@ -224,8 +244,11 @@ public function get($number, $value_name)
224244
return false;
225245
}
226246

247+
$number = (string)$number;
227248
foreach ($this->entryCache as $entry) {
228-
if ($entry->getArgument('name') === (string)$number) {
249+
if ($entry->getArgument('name') === $number
250+
|| $entry->getArgument('.id') === $number
251+
) {
229252
return $entry->getArgument($value_name);
230253
}
231254
}
@@ -403,18 +426,17 @@ public function filePutContents($filename, $data)
403426
* Executes a RouterOS script, written as a string.
404427
* Note that in cases of errors, the line numbers will be off, because the
405428
* script is executed at the current menu as context, with the specified
406-
* variables pre declared. This is achieved by prepending 2+count($params)
429+
* variables pre declared. This is achieved by prepending 1+count($params)
407430
* lines before your actual script.
408431
*
409432
* @param string $source A script to execute.
410433
* @param array $params An array of local variables to make available in
411434
* the script. Variable names are array keys, and variable values are
412-
* array values. Invalid names will not be added, and silently ignored.
413-
* Note that the script's (generated) name is always declared at the
414-
* variable "_".
435+
* array values. Note that the script's (generated) name is always added
436+
* as the variable "_", which you can overwrite here.
415437
* @param string $policy Allows you to specify a policy the script must
416-
* follow. Accepts the same things as in terminal. If left empty, the
417-
* script has no restrictions.
438+
* follow. Has the same format as in terminal. If left empty, the script
439+
* has no restrictions.
418440
* @param string $name The script is executed after being saved in
419441
* "/system script" under a random name, and is removed after execution.
420442
* To eliminate any possibility of name clashes, you can specify your
@@ -473,7 +495,7 @@ public function exec(
473495
* @return ResponseCollection returns the response collection, allowing you
474496
* to inspect errors, if any.
475497
*/
476-
protected function doBulk($what, array $args)
498+
protected function doBulk($what, array $args = array())
477499
{
478500
$bulkRequest = new Request($this->menu . '/' . $what);
479501
$bulkRequest->setArgument(
@@ -491,17 +513,17 @@ protected function doBulk($what, array $args)
491513
protected function refreshEntryCache()
492514
{
493515
if (null === $this->entryCache) {
494-
$this->entryCache = array();
516+
$entryCache = array();
495517
foreach ($this->client->sendSync(
496518
new Request($this->menu . '/print')
497-
) as $response) {
498-
$this->entryCache[
499-
hexdec(substr($response->getAttribute('.id'), 1))
519+
)->getAllOfType(Response::TYPE_DATA) as $response) {
520+
$entryCache[
521+
hexdec(substr($response->getArgument('.id'), 1))
500522
] = $response;
501523
}
502-
ksort($this->entryCache, SORT_NUMERIC);
524+
ksort($entryCache, SORT_NUMERIC);
503525
$this->entryCache = SplFixedArray::fromArray(
504-
array_values($this->entryCache)
526+
array_values($entryCache)
505527
);
506528
}
507529
}

tests/UtilFeaturesTest.php

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
namespace PEAR2\Net\RouterOS;
3+
4+
class UtilFeaturesTest extends \PHPUnit_Framework_TestCase
5+
{
6+
const REGEX_ID = '\*[A-F0-9]+';
7+
const REGEX_IDLIST = '/^(\*[A-F0-9]+\,)*(\*[A-F0-9]+)?$/';
8+
9+
/**
10+
* @var Util
11+
*/
12+
protected $util;
13+
/**
14+
* @var Client
15+
*/
16+
protected $client;
17+
18+
protected function setUp()
19+
{
20+
$this->util = new Util(
21+
$this->client = new Client(\HOSTNAME, USERNAME, PASSWORD, PORT)
22+
);
23+
}
24+
25+
protected function tearDown()
26+
{
27+
unset($this->util);
28+
unset($this->client);
29+
}
30+
31+
public function testChangeMenu()
32+
{
33+
$this->assertSame('/', $this->util->changeMenu());
34+
$this->assertSame('/', $this->util->changeMenu('queue'));
35+
$this->assertSame('/queue', $this->util->changeMenu('simple'));
36+
$this->assertSame('/queue/simple', $this->util->changeMenu('.. tree'));
37+
$this->assertSame('/queue/tree', $this->util->changeMenu('../type'));
38+
$this->assertSame('/queue/type', $this->util->changeMenu('/interface'));
39+
$this->assertSame('/interface', $this->util->changeMenu('/ip/arp'));
40+
$this->assertSame('/ip/arp', $this->util->changeMenu('/ip hotspot'));
41+
$this->assertSame('/ip/hotspot', $this->util->changeMenu());
42+
}
43+
44+
public function testFindByQuery()
45+
{
46+
$this->util->changeMenu('/queue/simple');
47+
$this->assertRegExp(
48+
'/^' . self::REGEX_ID . '$/',
49+
$this->util->find(
50+
Query::where('target-addresses', HOSTNAME_INVALID . '/32')
51+
)
52+
);
53+
}
54+
55+
public function testFindNoCriteria()
56+
{
57+
$this->util->changeMenu('/queue/simple');
58+
$findResults = $this->util->find();
59+
$this->assertRegExp(
60+
self::REGEX_IDLIST,
61+
$findResults
62+
);
63+
$this->assertSame(
64+
count(explode(',', $findResults)),
65+
count(
66+
$this->client->sendSync(
67+
new Request('/queue/simple/print')
68+
)->getAllOfType(Response::TYPE_DATA)
69+
)
70+
);
71+
}
72+
73+
public function testFindCallback()
74+
{
75+
$this->util->changeMenu('/queue/simple');
76+
$findResults = $this->util->find(
77+
function ($entry) {
78+
return $entry->getArgument(
79+
'target-addresses'
80+
) === HOSTNAME_INVALID . '/32';
81+
}
82+
);
83+
$this->assertRegExp(
84+
'/^' . self::REGEX_ID . '$/',
85+
$findResults
86+
);
87+
$this->assertSame(
88+
$findResults,
89+
$this->client->sendSync(
90+
new Request(
91+
'/queue/simple/print',
92+
Query::where('target-addresses', HOSTNAME_INVALID . '/32')
93+
)
94+
)->getArgument('.id')
95+
);
96+
}
97+
98+
public function testByCallbackName()
99+
{
100+
include 'data://text/plain;base64,' . base64_encode(
101+
<<<HEREDOC
102+
<?php
103+
function isHostnameInvalid(\$entry) {
104+
return \$entry->getArgument(
105+
'target-addresses'
106+
) === \PEAR2\Net\RouterOS\HOSTNAME_INVALID . '/32';
107+
}
108+
HEREDOC
109+
);
110+
$this->util->changeMenu('/queue/simple');
111+
$findResults = $this->util->find('isHostnameInvalid');
112+
$this->assertRegExp(
113+
'/^' . self::REGEX_ID . '$/',
114+
$findResults
115+
);
116+
$this->assertSame(
117+
$findResults,
118+
$this->client->sendSync(
119+
new Request(
120+
'/queue/simple/print',
121+
Query::where('target-addresses', HOSTNAME_INVALID . '/32')
122+
)
123+
)->getArgument('.id')
124+
);
125+
}
126+
127+
public function testFindById()
128+
{
129+
$originalResult = $this->client->sendSync(
130+
new Request(
131+
'/queue/simple/print',
132+
Query::where('target-addresses', HOSTNAME_INVALID . '/32')
133+
)
134+
);
135+
$this->assertSame(
136+
$originalResult->getArgument('.id'),
137+
$this->util->find($originalResult->getArgument('.id'))
138+
);
139+
}
140+
}

tests/phpunit.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
<file>RequestHandlingTest.php</file>
9999
<file>ClientFeaturesTest.php</file>
100100
<file>ClientPersistentFeaturesTest.php</file>
101-
<file>StateAlteringFeaturesTest.php</file>
101+
<file>ClientStateAlteringFeaturesTest.php</file>
102102
</testsuite>
103103
</testsuites>
104104
<filter>

0 commit comments

Comments
 (0)