The Case of the Incompatible Safe

When I entered our rooms, I found C. Auguste Dupin lounging in his fauteuil by the window. His eyes were on the paper in his hand, but he seemed to be gazing beyond it in abstracted thought.

"A death?" I asked, smiling, remembering the events of a few days previous.

"Non, mon ami, simply a puzzle, and a rather pretty one. It appears that one M. Tueur, in attempting to improve his Perl, has entrapped himself in a web of conflicting requirements from which he can not extricate himself."

"Better him than me," I observed. "How did this come about?"

"Very simply. He found himself in a position where he had a string containing Data::Dumper output, which he needed to load back into his code. Now, normally this requires a stringy eval, but since he was justly cautious about doing this, he decided to use the Safe module instead."

"A wise precaution," I remarked, trying to hold up my end of the conversation.

"But then he wanted to do coverage testing."

"Another wise decision. It is a well-known axiom that untested code is buggy code. But when do the conflicting requirements come in?"

"Ah, they have already done so, mon ami. Devel::Cover, in order not to pollute the name spaces in which it runs, makes fully-qualified calls to itself, which can not be resolved from inside a Safe compartment. So however great his test coverage might have been, by simply trying to measure it he ensures that code below his calls to Safe's reval() method is not executed."

"By blazes! That is a poser. What can he do? I suppose he could change his code to detect the loading of Devel::Cover, and fall back to a stringy eval. But no, you don't want to change production code just to test it."

"Non, non that is never wise," replied Dupin.

"Wait. He could write a mock Safe object, and use that in his testing. His new() would return a blessed reference to an empty hash, and his reval() could simply do a stringy eval on its argument. Other methods could simply return."

"How, then, will he know that his calls to Safe actually function as he expects? It may be that he needs to adjust the operand mask allowed by his Safe compartment. If he uses a mock object in his normal testing, he will not discover this."

"You're right. Whatever he does he gives up something. Do you have any thoughts, Dupin?"

"A small one only, I fear. Your idea of the mock object has merit, but it must be more selective in its application. Suppose M. Tueur had a way to use the mock object only when he is doing coverage testing, but without modifying his production code?"

"Can this thing be done be done?" I asked. "It seems impossible".

"Not impossible. He can simply place his mock object in a directory of its own, mock/cover/ for example, and insert that directory into @INC only when he is doing coverage testing.

"There are a number of ways to do this. He could detect the loading of Devel::Cover in his test modules, and modify @INC there, either directly or by calling lib->import().

"But if he is using Module::Build, a better way is simply to subclass that module, and add the following code to the subclass:


sub ACTION_testcover {
    my ( $self, @args ) = @_;
    local @INC = ( 'mock/cover', @INC );
    return $self->SUPER::ACTION_testcover( @args );
}

"In this way, the change is centralized to the one place where it is
known that a coverage test is being requested."

"Well, I'll be ..." But I was at a loss for words, and the sentence was left dangling in the air of Paris.

I confess to being not entirely satisfied with the solution that I have placed into the mouth of M. Dupin (which is a workaround rather than a real solution), and am left with the feeling that the shade of Edgar Allen Poe, in his capacity as analyst of Maelzel's chess-playing engine, deserves better. But after a day or so of ringing the changes on Safe's share_from() method, a mock object was the best I could do. Maybe there is a simpler solution, but the RT queue for the Safe distribution says that at least one other person is as fuddled as I am. Readers?

5 Comments

I cannot fathom how awesome these posts are... :)

Unfortunately I'm not familiar with Safe at all, so I can't advise much.

Being a crime novels (and TV shows) fan, I get a tingling little feeling every time I see a post like this (especially with titles that begin with "the case of"). Kinky? I think so!

Anyway, I don't think you need to limit it to the really really weird cases. I think there's a lot of stuff that beginners do not understand, and while experts might see through (such as me when sometimes I guess the killer right away, but not always), beginners might still learn from it in a *very* compelling way.

I should probably point out I still enjoy reading/watching the rest even when I know the end-result. Sometimes the story is rather how you made a discovery, ya know? :)

The Opcode module may provide a way out of M. Tueur's predicament.

Leave a comment

About Tom Wyant

user-pic I blog about Perl.