Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4075337
MemoizedCallableTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
MemoizedCallableTest.php
View Options
<?php
namespace
Wikimedia\Tests
;
use
InvalidArgumentException
;
use
MediaWikiCoversValidator
;
use
MemoizedCallable
;
use
PHPUnit\Framework\TestCase
;
use
stdClass
;
use
Wikimedia\TestingAccessWrapper
;
/**
* @covers \MemoizedCallable
*/
class
MemoizedCallableTest
extends
TestCase
{
use
MediaWikiCoversValidator
;
/**
* The memoized callable should relate inputs to outputs in the same
* way as the original underlying callable.
*/
public
function
testReturnValuePassedThrough
()
{
$mock
=
$this
->
getMockBuilder
(
stdClass
::
class
)
->
addMethods
(
[
'reverse'
]
)->
getMock
();
$mock
->
method
(
'reverse'
)
->
willReturnCallback
(
'strrev'
);
$memoized
=
new
MemoizedCallable
(
[
$mock
,
'reverse'
]
);
$this
->
assertEquals
(
'flow'
,
$memoized
->
invoke
(
'wolf'
)
);
}
/**
* Consecutive calls to the memoized callable with the same arguments
* should result in just one invocation of the underlying callable.
*
* @requires extension apcu
*/
public
function
testCallableMemoized
()
{
$observer
=
$this
->
getMockBuilder
(
stdClass
::
class
)
->
addMethods
(
[
'computeSomething'
]
)->
getMock
();
$observer
->
expects
(
$this
->
once
()
)
->
method
(
'computeSomething'
)
->
willReturn
(
'ok'
);
$memoized
=
new
ArrayBackedMemoizedCallable
(
[
$observer
,
'computeSomething'
]
);
// First invocation -- delegates to $observer->computeSomething()
$this
->
assertEquals
(
'ok'
,
$memoized
->
invoke
()
);
// Second invocation -- returns memoized result
$this
->
assertEquals
(
'ok'
,
$memoized
->
invoke
()
);
}
public
function
testInvokeVariadic
()
{
$memoized
=
new
MemoizedCallable
(
'sprintf'
);
$this
->
assertEquals
(
$memoized
->
invokeArgs
(
[
'this is %s'
,
'correct'
]
),
$memoized
->
invoke
(
'this is %s'
,
'correct'
)
);
}
public
function
testShortcutMethod
()
{
$this
->
assertEquals
(
'this is correct'
,
MemoizedCallable
::
call
(
'sprintf'
,
[
'this is %s'
,
'correct'
]
)
);
}
/**
* Outlier TTL values should be coerced to range 1 - 86400.
*/
public
function
testTTLMaxMin
()
{
$memoized
=
TestingAccessWrapper
::
newFromObject
(
new
MemoizedCallable
(
'abs'
,
100000
)
);
$this
->
assertEquals
(
86400
,
$memoized
->
ttl
);
$memoized
=
TestingAccessWrapper
::
newFromObject
(
new
MemoizedCallable
(
'abs'
,
-
10
)
);
$this
->
assertSame
(
1
,
$memoized
->
ttl
);
}
public
static
function
makeA
()
{
return
'a'
;
}
public
static
function
makeB
()
{
return
'b'
;
}
public
static
function
makeRand
()
{
return
rand
();
}
/**
* Closure names should be distinct.
*/
public
function
testMemoizedClosure
()
{
$a
=
new
MemoizedCallable
(
[
self
::
class
,
'makeA'
]
);
$b
=
new
MemoizedCallable
(
[
self
::
class
,
'makeB'
]
);
$this
->
assertEquals
(
'a'
,
$a
->
invokeArgs
()
);
$this
->
assertEquals
(
'b'
,
$b
->
invokeArgs
()
);
$a
=
TestingAccessWrapper
::
newFromObject
(
$a
);
$b
=
TestingAccessWrapper
::
newFromObject
(
$b
);
$this
->
assertNotEquals
(
$a
->
callableName
,
$b
->
callableName
);
$c
=
new
ArrayBackedMemoizedCallable
(
[
self
::
class
,
'makeRand'
]
);
$this
->
assertEquals
(
$c
->
invokeArgs
(),
$c
->
invokeArgs
(),
'memoized random'
);
}
public
function
testNonScalarArguments
()
{
$memoized
=
new
MemoizedCallable
(
'gettype'
);
$this
->
expectExceptionMessage
(
"non-scalar argument"
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$memoized
->
invoke
(
(
object
)[]
);
}
public
function
testUnnamedCallable
()
{
$this
->
expectExceptionMessage
(
'Cannot memoize unnamed closure'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$memoized
=
new
MemoizedCallable
(
static
function
()
{
return
'a'
;
}
);
}
public
function
testNotCallable
()
{
$this
->
expectExceptionMessage
(
"must be an instance of callable"
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$memoized
=
new
MemoizedCallable
(
14
);
}
}
/**
* A MemoizedCallable subclass that stores function return values
* in an instance property rather than APC or APCu.
*/
class
ArrayBackedMemoizedCallable
extends
MemoizedCallable
{
/** @var array */
private
$cache
=
[];
protected
function
fetchResult
(
$key
,
&
$success
)
{
if
(
array_key_exists
(
$key
,
$this
->
cache
)
)
{
$success
=
true
;
return
$this
->
cache
[
$key
];
}
$success
=
false
;
return
false
;
}
protected
function
storeResult
(
$key
,
$result
)
{
$this
->
cache
[
$key
]
=
$result
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 17 2026, 18:17 (10 w, 1 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d6/39/ac027bb568d7bac72d9c2049d45a
Default Alt Text
MemoizedCallableTest.php (4 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment