How can I mock %ENV in Perl tests? - perl

I'm trying to retrofit some tests using Test::More to legacy code and I've bumped into a bit of a snag. I don't seem to be able to set %ENV in the test module. The called function definitely uses this variable so %ENV doesn't seem to be carried across to the test object.
#!/usr/bin/perl
use strict; use warnings;
use Test::More qw(no_plan);
BEGIN {
$ENV{HTTP_WWW_AUTHENTICATE} =
'WWW-Authenticate: MyType realm="MyRealm",userid="123",password="abc"';
use_ok('Util');
}
$ENV{HTTP_WWW_AUTHENTICATE} =
'WWW-Authenticate: MyType realm="MyRealm",userid="123",password="abc"';
printf qq{get_authentication_info = "%s"\n}, get_authentication_info();
ok(get_authentication_info(), 'Get authentication info');
I keep getting...
perl t\Util.t
ok 1 - use Util;
Use of uninitialized value in concatenation (.) or string at t\Util.t line 14.
get_authentication_info = ""
As with all things Perl, I'm pretty sure that some one has done this before.
UPDATE: Thanks to all for your help
The problem was between the keyboard & chair ... My test data was just plain wrong
It needed to be
$ENV{HTTP_WWW_AUTHENTICATE} =
'MyType realm="MyRealm",userid="123",password="abc"';

As Sinan said, the $ENV{...} lines are commented out, so it can't work. But if you want really testable code, I'd suggest to make the get_authentication_info function take a hash as an argument. That way you can test it without setting the global variable, and in the real code you can pass the real enviromnent hash. Global state will always become a problem eventually.

What does
get_authentication_info()
return?
My guess is nothing.
If this is always true, then line 14 will always return the "Use of uninitialized value..." warning.
If you expect a value, you need to investigate why get_authentication_info() is failing?

Agreed with Lukáš -- get your global environment (and perform validity checks etc) all in one place, such as in its own method, and then pass those values to all other methods that need it. That way in your unit tests you can just drop in a replacement method that determines the environment and config variables in a different way (such as from a file, or directly set at the top of your test script).

Why are the lines setting $ENV{HTTP_WWW_AUTHENTICATE} commented out?
Also, what are the specs for get_authentication_info()?

Try setting the env variable before BEGIN.
If not try this:
First, go to a command prompt and set the env var there. Then run your script. If the tests pass. Then as you predicted, the problem is with setting the env var.
If the tests fail, then the problem lies some where else (probably in get_authentication_info).

Related

Specifying a Perl 6 class in a variable

I have a bunch of Perl 6 tests that start off with some basic tests where I put the class name to test in a variable the use that variable throughout the test:
my $package = 'Some::Class';
use-ok $package;
my $class = ::($package);
can-ok $class, 'new';
I hadn't paid attention to this for a bit, but it no longer works because classes are lexically loaded now:
No such symbol 'Some::Class'
It's not a hard fix. Load the module without use-ok and in the scope where I want ::($package):
use Some::Class;
...
The other solutions (discounting an ugly EVAL perhaps) have the issue I'm trying to avoid.
But, I don't particularly like that since the name shows up twice in the file. I've particularly liked my formerly-working idiom that I carried over from Perl 5. If I wanted to change a class name, it only showed up once in the file. I could easily generate boilerplate tests (although, it's not that much harder for the fix).
Is there a way I can get back to the ideal I wanted? (Although I figure lexical loading in the next version will get in the way again).
To sum up the problem: You want to load modules using a symbol instead of hardcoding it.
Using a constant should do this for you:
constant some-module = 'Some::Module';
use ::(some-module);
You can also load the module at runtime using require, which would allow runtime computed values:
my $some-module = 'Some::Module';
require ::($some-module);
::($some-module).foo
It would make sense to do this after trying a use-ok.
For extra credit, you may find the techniques in this article useful.
http://rakudo.org/2017/03/18/lexical-require-upgrade-info/

How to use one module in another module in perl?

Iam writing a perl script ,in which iam using a module utils.pm and in utils.pm iam using another module DB.pm in which i have a sub routine connetToDB().
in utils.pm iam writing
use DB qw (connectToDB());
and below iam calling that subroutine as
my $connection=DB::connectToDB(); (This is line 30)
it is giving an error like follows. Can someone pls help?
Undefined subroutine &DB::connectToDB called at utils.pm line 30.
you can see the DB.pm code here
The direct error in the shown code is that inside qw() you need names. The use pragma
Imports some semantics into the current package from the named module
(my emphasis). The "connectToDB()", with parentheses, is not the correct name for the subroutine. The error message simply says that it didn't find such a sub.
So just drop the parens, use DB qw(connectToDB);.
The code for the package was added to the question and here are some comments.
A similar fix is needed with your #EXPORT: you need the subroutine names (lose &).
Perhaps more importantly, you defined the sub using prototypes. Your sub is consistent with the prototype you use so I'll assume that it's done on purpose.
This is a very advanced (mis?)feature, which is very different from similar looking devices in other languages and is normally not needed. Chances are that you expect wrong things from prototypes. Go search for it. I'd advise against.
A side note: the prototype-related () and & are not a part of the subroutine name.
The last executed statement that returns in a module must return true, or code won't compile. The convention to ensure this is to put 1; at the end of the package.
Finally, you shouldn't name the module DB as that namespace is used internally by Perl. Also, such a generic name is just not good for a module -- it makes it easy to run into conflicts.
use DB qw(connectToDB);
my $connection=DB->connectToDB();
or
if you have defined a constructor "new" in DB.pm module then
my $connection=DB->new();
my $result = $connection->connectToDB();

What happens if I reference a package but don't use/require it?

As much as I can (mostly for clarity/documentation), I've been trying to say
use Some::Module;
use Another::Module qw( some namespaces );
in my Perl modules that use other modules.
I've been cleaning up some old code and see some places where I reference modules in my code without ever having used them:
my $example = Yet::Another::Module->AFunction($data); # EXAMPLE 1
my $demo = Whats::The::Difference::Here($data); # EXAMPLE 2
So my questions are:
Is there a performance impact (I'm thinking compile time) by not stating use x and simply referencing it in the code?
I assume that I shouldn't use modules that aren't utilized in the code - I'm telling the compiler to compile code that is unnecessary.
What's the difference between calling functions in example 1's style versus example 2's style?
I would say that this falls firmly into the category of preemptive optimisation and if you're not sure, then leave it in. You would have to be including some vast unused libraries if removing them helped at all
It is typical of Perl to hide a complex issue behind a simple mechanism that will generally do what you mean without too much thought
The simple mechanisms are these
use My::Module 'function' is the same as writing
BEGIN {
require My::Module;
My::Module->import( 'function' );
}
The first time perl successfully executes a require statement, it adds an element to the global %INC hash which has the "pathified" module name (in this case, My/Module.pm) for a key and the absolute location where it found the source as a value
If another require for the same module is encountered (that is, it already exists in the %INC hash) then require does nothing
So your question
What happens if I reference a package but don't use/require it?
We're going to have a problem with use, utilise, include and reference here, so I'm code-quoting only use and require when I mean the Perl language words.
Keeping things simple, these are the three possibilities
As above, if require is seen more than once for the same module source, then it is ignored after the first time. The only overhead is checking to see whether there is a corresponding element in %INC
Clearly, if you use source files that aren't needed then you are doing unnecessary compilation. But Perl is damn fast, and you will be able to shave only fractions of a second from the build time unless you have a program that uses enormous libraries and looks like use Catalyst; print "Hello, world!\n";
We know what happens if you make method calls to a class library that has never been compiled. We get
Can't locate object method "new" via package "My::Class" (perhaps you forgot to load "My::Class"?)
If you're using a function library, then what matters is the part of use that says
My::Module->import( 'function' );
because the first part is require and we already know that require never does anything twice. Calling import is usually a simple function call, and you would be saving nothing significant by avoiding it
What is perhaps less obvious is that big modules that include multiple subsidiaries. For instance, if I write just
use LWP::UserAgent;
then it knows what it is likely to need, and these modules will also be compiled
Carp
Config
Exporter
Exporter::Heavy
Fcntl
HTTP::Date
HTTP::Headers
HTTP::Message
HTTP::Request
HTTP::Response
HTTP::Status
LWP
LWP::MemberMixin
LWP::Protocol
LWP::UserAgent
Storable
Time::Local
URI
URI::Escape
and that's ignoring the pragmas!
Did you ever feel like you were kicking your heels, waiting for an LWP program to compile?
I would say that, in the interests of keeping your Perl code clear and tidy, it may be an idea to remove unnecessary modules from the compilation phase. But don't agonise over it, and benchmark your build times before doing any pre-handover tidy. No one will thank you for reducing the build time by 20ms and then causing them hours of work because you removed a non-obvious requirement.
You actually have a bunch of questions.
Is there a performance impact (thinking compile time) by not stating use x and simply referencing it in the code?
No, there is no performance impact, because you can't do that. Every namespace you are using in a working program gets defined somewhere. Either you used or required it earlier to where it's called, or one of your dependencies did, or another way1 was used to make Perl aware of it
Perl keeps track of those things in symbol tables. They hold all the knowledge about namespaces and variable names. So if your Some::Module is not in the referenced symbol table, Perl will complain.
I assume that I shouldn't use modules that aren't utilized in the code - I'm telling the compiler to compile code that is unnecessary.
There is no question here. But yes, you should not do that.
It's hard to say if this is a performance impact. If you have a large Catalyst application that just runs and runs for months it doesn't really matter. Startup cost is usually not relevant in that case. But if this is a cronjob that runs every minute and processes a huge pile of data, then an additional module might well be a performance impact.
That's actually also a reason why all use and require statements should be at the top. So it's easy to find them if you need to add or remove some.
What's the difference between calling functions in example 1's style versus example 2's style?
Those are for different purposes mostly.
my $example = Yet::Another::Module->AFunction($data); # EXAMPLE 1
This syntax is very similar to the following:
my $e = Yet::Another::Module::AFunction('Yet::Another::Module', $data)
It's used for class methods in OOP. The most well-known one would be new, as in Foo->new. It passes the thing in front of the -> to the function named AFunction in the package of the thing on the left (either if it's blessed, or if it's an identifier) as the first argument. But it does more. Because it's a method call, it also takes inheritance into account.
package Yet::Another::Module;
use parent 'A::First::Module';
1;
package A::First::Module;
sub AFunction { ... }
In this case, your example would also call AFunction because it's inherited from A::First::Module. In addition to the symbol table referenced above, it uses #ISA to keep track of who inherits from whom. See perlobj for more details.
my $demo = Whats::The:Difference::Here($data); # EXAMPLE 2
This has a syntax error. There is a : missing after The.
my $demo = Whats::The::Difference::Here($data); # EXAMPLE 2
This is a function call. It calls the function Here in the package Whats::The::Difference and passes $data and nothing else.
Note that as Borodin points out in a comment, your function names are very atypical and confusing. Usually functions in Perl are written with all lowercase and with underscores _ instead of camel case. So AFunction should be a_function, and Here should be here.
1) for example, you can have multiple package definitions in one file, which you should not normally do, or you could assign stuff into a namespace directly with syntax like *Some::Namespace::frobnicate = sub {...}. There are other ways, but that's a bit out of scope for this answer.

How can I make each instance of a perl script have its own unique id?

I have a perl script that connects to the Plex API. It logs in and performs certain actions (mostly working).
However, the Plex API suggests (insists?) that each instance of the script send a unique ID, such that if I share this script with anyone else they should use a different string.
In the interests of keeping this simple, I don't want to have some configuration file that keeps that value outside of the script. I also can't leave a value hard-coded in, no one who downloads this will change it.
Could the perl script modify itself?
If I were to declare it as such:
my $uuid = 1;
... then could I not check immediately afterward if this value is equal to 1, and if so overwrite that with a randomly generated uuid? The script would then exit, but somehow re-invoke itself (so the user doesn't have to run it a second time).
Is there a safe way to do this? Alternatively, is there a better way to accomplish the goal without using this method?
Make the last line of your script __DATA__ and append the ID to the script either at installation or first run. Reading from the special <DATA> handle reads the data segment of a script.
You could use UUID::Tiny to generate a random UUID:
use UUID::Tiny;
my $uuid = create_UUID(UUID_V4);
To preserve the UUID between invocations, you'll have to modify the script itself. The answers in this thread might be helpful.
Update
You say in the comments that you want a different unique ID "per installation", but you also say that "it needs to be the same value for any given user", so I'm no longer sure that my answer will satisfy your requirements
I suggest that you use the system UUID returned by dmidecode. Of course you will need to have it installed on your computer, and there's a parser module for it on CPAN called Parse::DMIDecode
It's slightly more complex if you have to support Windows systems. You can use DmiDecode for Windows, which is available as a ready-built binary, but the parser module explicitly checks that there are no colons (amongst other things) in the path to the demidecode executable, so a call to the probe method won't work. Instead you must call demidecode and pass the result to the parse method
This short example works fine on both Linux and Windows
use strict;
use warnings 'all';
use feature 'say';
use Parse::DMIDecode;
my $decoder = Parse::DMIDecode->new;
$decoder->parse(qx{dmidecode});
say $decoder->keyword('system-uuid');
output
35304535-3439-4344-3232-3245FFFFFFFF

Perl: variable scope issue with CGI & DBI modules

I've run into what appears to be a variable scope issue I haven't encountered before. I'm using Perl's CGI module and a call to DBI's do() method. Here's the code structure, simplified a bit:
use DBI;
use CGI qw(:cgi-lib);
&ReadParse;
my $dbh = DBI->connect(...............);
my $test = $in{test};
$dbh->do(qq{INSERT INTO events VALUES (?,?,?)},undef,$in{test},"$in{test}",$test);
The #1 placeholder variable evaluates as if it is uninitialized. The other two placeholder variables work.
The question: Why is the %in hash not available within the context of do(), unless I wrap it in double quotes (#2 placeholder) or reassign the value to a new variable (#3 placeholder)?
I think it's something to do with how the CGI module's ReadParse() function assigns scope to the %in hash, but I don't know Perl scoping well enough to understand why %in is available at the top level but not from within my do() statement.
If someone does understand the scoping issue, is there a better way to handle it? Wrapping all the %in references in double quotes seems a little messy. Creating new variables for each query parameter isn't realistic.
Just to be clear, my question is about the variable scoping issue. I realize that ReadParse() isn't the recommended method to grab query params with CGI.
I'm using Perl 5.8.8, CGI 3.20, and DBI 1.52. Thank you in advance to anyone reading this.
#Pi & #Bob, thanks for the suggestions. Pre-declaring the scope for %in has no effect (and I always use strict). The result is the same as before: in the db, col1 is null while cols 2 & 3 are set to the expected value.
For reference, here's the ReadParse function (see below). It's a standard function that's part of CGI.pm. The way I understand it, I'm not meant to initialize the %in hash (other than satisfying strict) for purposes of setting scope, since the function appears to me to handle that:
sub ReadParse {
local(*in);
if (#_) {
*in = $_[0];
} else {
my $pkg = caller();
*in=*{"${pkg}::in"};
}
tie(%in,CGI);
return scalar(keys %in);
}
I guess my question is what is the best way to get the %in hash within the context of do()? Thanks again! I hope this is the right way to provide additional info to my original question.
#Dan: I hear ya regarding the &ReadParse syntax. I'd normally use CGI::ReadParse() but in this case I thought it was best to stick to how the CGI.pm documentation has it exactly.
It doesn't actually look like you're using it as described in the docs:
https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL
If you must use it, then CGI::ReadParse(); seems more sensible and less crufty syntax. Although I can't see it making much difference in this situation, but then it is a tied variable, so who the hell knows what it's doing ;)
Is there a particular reason you can't use the more-common $cgi->param('foo') syntax? It's a little bit cleaner, and filths up your namespace in a considerably more predictable manner..
use strict;. Always.
Try declaring
our %in;
and seeing if that helps. Failing that, strict may produce a more useful error.
I don't know what's wrong, but I can tell you some things that aren't:
It's not a scoping issue. If it were then none of the instances of $in{test} would work.
It's not the archaic & calling syntax. (It's not "right" but it's harmless in this case.)
ReadParse is a nasty bit of code. It munges the symbol table to create the global variable %in in the calling package. What's worse is that it's a tied variable, so accessing it could (theoretically) do anything. Looking at the source code for CGI.pm, the FETCH method just invokes the params() method to get the data. I have no idea why the fetch in the $dbh->do() isn't working.
Firstly, that is not in the context/scope of do. It is still in the context of main or global. You dont leave context until you enter {} in some way relating to subroutines or different 'classes' in perl. Within () parens you are not leaving scope.
The sample you gave us is of an uninitialized hash and as Pi has suggested, using strict will certainly keep those from occuring.
Can you give us a more representative example of your code? Where are you setting %IN and how?
Something's very broken there. Perl's scoping is relatively simple, and you're unlikely to stumble upon anything odd like that unless you're doing something daft. As has been suggested, switch on the strict pragma (and warnings, too. In fact you should be using both anyway).
It's pretty hard to tell what's going on without being able to see how %in is defined (is it something to do with that nasty-looking ReadParse call? why are you calling it with the leading &, btw? that syntax has been considered dead and gone for a long time). I suggest posting a bit more code, so we can see what's going on..
What version of DBI are you using? From looking at the DBI changelog it appears that versions prior to 1.00 didn't support the attribute argument. I suspect that the "uninitialized" $in{test} is actually the undef that you're passing to $dbh->do().
From the example you gave, this is not a scoping issue, or none of the parameters would work.
Looks like DBI (or a DBD, not sure where bind parameters are used) isn't honoring tie magic.
The workaround would be to stringize or copy what you pass to it, like your second and third parameters do.
A simple test using SQLite and DBI 1.53 shows it working ok:
$ perl -MDBI -we'sub TIEHASH { bless {} } sub FETCH { "42" } tie %x, "main" or die; my $dbh = DBI->connect("dbi:SQLite:dbname=dbfile","",""); $dbh->do("create table foo (bar char(80))"); $dbh->do("insert into foo values (?)", undef, $x{foo}); print "got: " . $dbh->selectrow_array("select bar from foo") . "\n"; $dbh->do("drop table foo")'
got: 42
Care to share what database you are using?
Per the DBI documentation: Binding a tied variable doesn't work, currently.
DBI is pretty complicated under the hood, and unfortunately goes through some gyrations to be efficient that are causing your problem. I agree with everyone else who says to get rid of the ugly old cgi-lib style code. It's unpleasant enough to do CGI without a nice framework (go Catalyst), let alone something that's been obsolete for a decade.
Okay, try this:
use CGI;
my %in;
CGI::ReadParse(\%in);
That might help as it's actually using a variable that you've declared, and therefore can control the scope of (plus it'll let you use strict without other nastiness that could be muddying the waters)
As this is starting to look like a tie() problem, try the following experiment. Save this as a foo.pl and run it as perl foo.pl "x=1"
use CGI;
CGI::ReadParse();
p($in{x}, "$in{x}");
sub p { my #a = #_; print "#a\n" }
It should print 1 1. If it doesn't, we've found the culprit.
I just tried your test codce from http://www.carcomplaints.com/test/test.pl.txt, and it works right away on my computer, no problems. I get three values as expected. I didn't run it as CGI, but using:
...
use CGI qw/-debug/;
...
I write a variable on the console (test=test) and your scripts inserts without a problem.
If however your leave this out, tt will insert an empty string and two NULLs. This is a because you interpolate a value into a string. This will makes a string with value of $in{test} which is undef at the moment. undef stringifies to an empty string, which is what is inserted into database.
Try this
%in = ReadParse();
but i doubt that. Are you trying to get query parameters or something?