1# Test::SNMP::Info 2# 3# Copyright (c) 2018 Eric Miller 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, 10# this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# * Neither the name of the University of California, Santa Cruz nor the 15# names of its contributors may be used to endorse or promote products 16# derived from this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22# LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28# POSSIBILITY OF SUCH DAMAGE. 29 30package Test::SNMP::Info; 31 32use strict; 33use warnings; 34use Test::Class::Most parent => 'My::Test::Class'; 35 36use SNMP::Info; 37 38sub constructor : Tests(+3) { 39 my $test = shift; 40 $test->SUPER::constructor; 41 42 is($test->{info}{snmp_comm}, 'public', 'SNMP comm arg saved'); 43 is($test->{info}{snmp_ver}, 2, 'SNMP version arg saved'); 44 is($test->{info}{snmp_user}, 'initial', 'SNMP user arg saved'); 45} 46 47sub update : Tests(9) { 48 my $test = shift; 49 50 can_ok($test->{info}, 'update'); 51 52 # Starting community 53 is($test->{info}{sess}{Community}, 'public', q(Original community 'public')); 54 55 # Change community 56 my %update_args = ('Community' => 'new_community',); 57 delete $test->{info}{args}{Session}; 58 ok($test->{info}->update(%update_args), 'Update community'); 59 is($test->{info}->error(), undef, '... and no error'); 60 is($test->{info}{sess}{Community}, 'new_community', 'Community changed'); 61 62TODO: { 63 # The update() method creates a new SNMP::Session, v1/2 do not actually 64 # need to contact the DestHost for session creation while v3 does. 65 # It appears that Net-SNMP 5.8 changes the behavior of v3 session creation 66 # so that it doesn't require contact with the DestHost to pass these tests 67 # We also could connect to http://snmplabs.com v3 simulator but would 68 # prefer to keep those tests isolated to 10_remote_snmplabs.t - we could 69 # also move the update() tests to that file. 70 71 my $version = $SNMP::VERSION; 72 my ( $major, $minor, $rev ) = split( '\.', $version ); 73 74 todo_skip "Skip v3 Context update() tests when using Net-SNMP < 5.8", 4 75 if ($major < 5 or $minor < 8); 76 77 # Starting context 78 ok(!defined $test->{info}{sess}{Context}, q(Context doesn't exist)); 79 80 # Change context 81 # Since update() is actually creating new SNMP::Session we can put 82 # whatever session arguments needed in %update_args 83 %update_args = ('Context' => 'vlan-100', 'Version' => 3,); 84 ok($test->{info}->update(%update_args), 'Update Context'); 85 is($test->{info}->error(), undef, '... and no error'); 86 is($test->{info}->{sess}{Context}, 'vlan-100', 'Context changed'); 87 88 } 89} 90 91sub cache_and_clear_cache : Tests(9) { 92 my $test = shift; 93 94 # Isolate tests to cache method. Populated structure of global 'name' and 95 # func 'i_description' 96 my $cache_data = { 97 '_name' => 'Test-Name', 98 '_i_description' => 1, 99 'store' => { 100 'i_description' => 101 {10 => 'Test-Description-10', 20 => 'Test-Description-20'} 102 } 103 }; 104 105 # The empty store hash exists upon initialization and remains when the cache 106 # is cleared. 107 my $empty_cache = {'store' => {}}; 108 109 can_ok($test->{info}, 'cache'); 110 cmp_deeply($empty_cache, $test->{info}->cache(), 'Cache starts empty'); 111 ok($test->{info}->cache($cache_data), 'Insert test data into cache'); 112 cmp_deeply( 113 $cache_data, 114 $test->{info}->cache(), 115 'Cache method returns test data' 116 ); 117 is($test->{info}->name(), 118 'Test-Name', 'Global method call returned cached data'); 119 cmp_deeply( 120 $test->{info}->i_description(), 121 $cache_data->{store}{i_description}, 122 'Funcs method call returned cached data' 123 ); 124 can_ok($test->{info}, 'clear_cache'); 125 ok($test->{info}->clear_cache(), 'cache cleared'); 126 cmp_deeply( 127 $empty_cache, 128 $test->{info}->cache(), 129 'No cached data returned after clear_cache method call' 130 ); 131} 132 133sub debug : Tests(4) { 134 my $test = shift; 135 136 can_ok($test->{info}, 'debug'); 137 138 ok( 139 defined $test->{info}{debug} 140 && $test->{info}{debug} == 0 141 && $test->{info}->debug() == 0, 142 'Debug initialized off' 143 ); 144 $test->{info}->debug(1); 145 ok($test->{info}{debug} && $test->{info}->debug(), 'Debug on'); 146 $test->{info}->debug(0); 147 ok($test->{info}{debug} == 0 && $test->{info}->debug() == 0, 'Debug off'); 148} 149 150sub offline : Tests(4) { 151 my $test = shift; 152 153 can_ok($test->{info}, 'offline'); 154 155 ok(!defined $test->{info}{Offline}, 'Offline not initialized'); 156 $test->{info}->offline(1); 157 ok($test->{info}{Offline} && $test->{info}->offline(), 'Offline mode on'); 158 $test->{info}->offline(0); 159 ok($test->{info}{Offline} == 0 && $test->{info}->offline() == 0, 160 'Offline off'); 161} 162 163sub bulkwalk : Tests(4) { 164 my $test = shift; 165 166 can_ok $test->{info}, 'bulkwalk'; 167 168 # Test harness initalizes BulkWalk off, if we didn't provide an arg 169 # it would not be defined. 170 ok( 171 !defined $test->{info}{BulkWalk} 172 || ($test->{info}{BulkWalk} == 0 && $test->{info}->bulkwalk() == 0), 173 'Bulkwalk initialized off' 174 ); 175 $test->{info}->bulkwalk(1); 176 ok($test->{info}{BulkWalk} && $test->{info}->bulkwalk(), 'Bulkwalk on'); 177 $test->{info}->bulkwalk(0); 178 ok($test->{info}{BulkWalk} == 0 && $test->{info}->bulkwalk() == 0, 179 'Bulkwalk off'); 180} 181 182sub loopdetect : Tests(4) { 183 my $test = shift; 184 185 can_ok $test->{info}, 'loopdetect'; 186 187 ok(!defined $test->{info}{LoopDetect}, 'Loopdetect not initialized'); 188 $test->{info}->loopdetect(1); 189 ok($test->{info}{LoopDetect} && $test->{info}->loopdetect(), 'Loopdetect on'); 190 $test->{info}->loopdetect(0); 191 ok($test->{info}{LoopDetect} == 0 && $test->{info}->loopdetect() == 0, 192 'Loopdetect off'); 193} 194 195sub device_type : Tests(+6) { 196 my $test = shift; 197 $test->SUPER::device_type(); 198 199 # No sysServices and unknown sysDescr results in SNMP::Info 200 my $cache_data 201 = {'_layers' => '00000000', '_description' => 'My-Test-sysDescr',}; 202 $test->{info}->cache($cache_data); 203 204 $test->{info}->debug(1); 205 warnings_like { $test->{info}->device_type() } 206 [{carped => qr/Might give unexpected results/i}], 207 'No sysServices and unknown sysDescr with debug on gives warning'; 208 $test->{info}->debug(0); 209 $test->{info}->clear_cache(); 210 211 # Cache has been cleared, empty args and no SNMP data result in undef 212 is($test->{info}->device_type(), 213 undef, 'No sysServices, no sysDescr results in undef'); 214 215 # Test one oid per layer hash just to verify oid mapping, no need to test 216 # every hash key - chose an id that is unique per layer 217 218 # Layer 3 219 $cache_data = { 220 '_layers' => 4, 221 '_description' => 'My-Test-sysDescr', 222 '_id' => '.1.3.6.1.4.1.18' 223 }; 224 $test->{info}->cache($cache_data); 225 is($test->{info}->device_type, 226 'SNMP::Info::Layer3::BayRS', 'Layer 3 device type by sysObjectID'); 227 $test->{info}->clear_cache(); 228 229 # Layer 2 230 $cache_data = { 231 '_layers' => 2, 232 '_description' => 'My-Test-sysDescr', 233 '_id' => '.1.3.6.1.4.1.11898' 234 }; 235 $test->{info}->cache($cache_data); 236 is($test->{info}->device_type, 237 'SNMP::Info::Layer2::Orinoco', 'Layer 2 device type by sysObjectID'); 238 $test->{info}->clear_cache(); 239 240 # Layer 1 241 $cache_data = { 242 '_layers' => 1, 243 '_description' => 'My-Test-sysDescr', 244 '_id' => '.1.3.6.1.4.1.2925' 245 }; 246 $test->{info}->cache($cache_data); 247 is( 248 $test->{info}->device_type, 249 'SNMP::Info::Layer1::Cyclades', 250 'Layer 1 device type by sysObjectID' 251 ); 252 $test->{info}->clear_cache(); 253 254 # Layer 7 255 $cache_data = { 256 '_layers' => 64, 257 '_description' => 'My-Test-sysDescr', 258 '_id' => '.1.3.6.1.4.1.318' 259 }; 260 $test->{info}->cache($cache_data); 261 is($test->{info}->device_type, 262 'SNMP::Info::Layer7::APC', 'Layer 7 device type by sysObjectID'); 263 $test->{info}->clear_cache(); 264 265 # We will test each specific subclass, so no need to check that logic here 266} 267 268sub error : Tests(7) { 269 my $test = shift; 270 271 can_ok($test->{info}, 'error'); 272 ok(!exists $test->{info}{error}, 'Error not present'); 273 $test->{info}{error} = 'Test Error'; 274 is($test->{info}->error(), 'Test Error', 'Test Error present'); 275 is($test->{info}->error(), undef, 'Test Error cleared upon read'); 276 $test->{info}{error} = 'Test Error 2'; 277 is($test->{info}->error(1), 278 'Test Error 2', 'Test Error 2 present and no clear flag set'); 279 is($test->{info}->error(0), 280 'Test Error 2', 'Test Error 2 still present on next read'); 281 is($test->{info}->error(), 282 undef, 'Test Error 2 cleared upon read with flag set to false'); 283} 284 285sub has_layer : Tests(6) { 286 my $test = shift; 287 288 can_ok $test->{info}, 'has_layer'; 289 $test->{info}->clear_cache(); 290 291 # Populate cache, one key/value so don't bother going through the 292 # cache() method. 293 # Layers holds the unmunged value (decimal) 294 $test->{info}{'_layers'} = 1; 295 is($test->{info}->has_layer(1), 1, 'Has layer 1'); 296 297 $test->{info}{'_layers'} = 2; 298 is($test->{info}->has_layer(2), 1, 'Has layer 2'); 299 300 $test->{info}{'_layers'} = 4; 301 is($test->{info}->has_layer(3), 1, 'Has layer 3'); 302 303 # We don't use layers 4-6 for classification, skip testing 304 305 $test->{info}{'_layers'} = 64; 306 is($test->{info}->has_layer(7), 1, 'Has layer 7'); 307 308 # Check for undef layers 309 $test->{info}{'_layers'} = undef; 310 is($test->{info}->has_layer(7), undef, 'Undef layers returns undef'); 311} 312 313sub snmp_comm : Tests(4) { 314 my $test = shift; 315 316 can_ok $test->{info}, 'snmp_comm'; 317 318 # Define before test to be sure instead of relying on initalization 319 $test->{info}{snmp_comm} = 'publicv1'; 320 $test->{info}{snmp_ver} = 1; 321 is($test->{info}->snmp_comm(), 'publicv1', 322 'Version 1 returns SNMP community'); 323 324 $test->{info}{snmp_comm} = 'publicv2'; 325 $test->{info}{snmp_ver} = 2; 326 is($test->{info}->snmp_comm(), 'publicv2', 327 'Version 2 returns SNMP community'); 328 329 $test->{info}{snmp_user} = 'initialv3'; 330 $test->{info}{snmp_ver} = 3; 331 is($test->{info}->snmp_comm(), 'initialv3', 'Version 3 returns SNMP user'); 332} 333 334sub snmp_ver : Tests(2) { 335 my $test = shift; 336 337 can_ok $test->{info}, 'snmp_ver'; 338 339 # Define before test to be sure instead of relying on initalization 340 $test->{info}{snmp_ver} = 1; 341 is($test->{info}->snmp_ver(), 1, 'SNMP version returned'); 342} 343 344sub specify : Tests(4) { 345 my $test = shift; 346 347 can_ok($test->{info}, 'specify'); 348 $test->{info}->cache_clear(); 349 350 # Specify uses device_type(), use same data as that test to setup 351 # test cases here since return values from device_type() with them 352 # have been tested 353 354 # device_type returns undef 355 $test->{info}->specify(); 356 is( 357 $test->{info}->error(), 358 'SNMP::Info::specify() - Could not get info from device', 359 'Undef device type throws error' 360 ); 361 $test->{info}->cache_clear(); 362 363 # Populate cache for following tests 364 my $cache_data 365 = {'_layers' => '00000000', '_description' => 'My-Test-sysDescr',}; 366 $test->{info}->cache($cache_data); 367 368 isa_ok($test->{info}->specify(), 369 'SNMP::Info', 'SNMP::Info device_type returns self'); 370 $test->{info}->cache_clear(); 371 372 # Layer 7 - SNMP::Info::Layer7::APC 373 $cache_data = { 374 '_layers' => 64, 375 '_description' => 'My-Test-sysDescr', 376 '_id' => '.1.3.6.1.4.1.318' 377 }; 378 $test->{info}->cache($cache_data); 379 isa_ok($test->{info}->specify(), 380 'SNMP::Info::Layer7::APC', 381 'Layer 7 device type returns new object of same type'); 382 $test->{info}->clear_cache(); 383} 384 385sub cisco_comm_indexing : Tests(2) { 386 my $test = shift; 387 388 can_ok $test->{info}, 'cisco_comm_indexing'; 389 is($test->{info}->cisco_comm_indexing(), 0, 'Cisco community indexing off'); 390} 391 392sub if_ignore : Tests(2) { 393 my $test = shift; 394 395 can_ok $test->{info}, 'if_ignore'; 396 cmp_deeply($test->{info}->if_ignore(), 397 {}, 'No ignored interfaces for this class'); 398} 399 400sub bulkwalk_no : Tests(2) { 401 my $test = shift; 402 403 can_ok $test->{info}, 'bulkwalk_no'; 404 is($test->{info}->bulkwalk_no(), 0, 'Bulkwalk not turned off in this class'); 405} 406 407sub i_speed : Tests(2) { 408 my $test = shift; 409 410 can_ok $test->{info}, 'i_speed'; 411 412 # Method uses partial fetches which ignores the cache and reloads data 413 # therefore we must use the mocked session. Populate the session data 414 # so that the mock_getnext() has data to fetch. 415 my $data = { 416 417 # Need to use OID for ifSpeed since it could resolve to a fully qualified 418 # name as either RFC1213-MIB::ifSpeed or IF-MIB::ifSpeed dependent upon 419 # which MIB got loaded last which is based upon random hash ordering. Using 420 # a fully qualified name with mock session we would need to know which MIB 421 # "owned" the OID since the MIB hash is indexed by OID. This is not an 422 # issue in live code since what is fed to getnext for a fully qualified 423 # name is what is returned. 424 '.1.3.6.1.2.1.2.2.1.5' => {38 => 0, 49 => 4294967295, 501 => 1000000000,}, 425 'IF-MIB::ifHighSpeed' => {38 => 0, 49 => 32000, 501 => 1000,}, 426 }; 427 my $expected = {38 => 0, 49 => '32 Gbps', 501 => '1.0 Gbps',}; 428 $test->{info}{sess}{Data} = $data; 429 cmp_deeply($test->{info}->i_speed(), 430 $expected, 'High speed interface reported accurately'); 431} 432 433sub i_speed_raw : Tests(3) { 434 my $test = shift; 435 436 can_ok $test->{info}, 'i_speed_raw'; 437 438 # Method uses partial fetches which ignores the cache and reloads data 439 # therefore we must use the mocked session. Populate the session data 440 # so that the mock_getnext() has data to fetch. 441 my $data = { 442 443 # Need to use OID for ifSpeed since it could resolve to a fully qualified 444 # name as either RFC1213-MIB::ifSpeed or IF-MIB::ifSpeed dependent upon 445 # which MIB got loaded last which is based upon random hash ordering. Using 446 # a fully qualified name with mock session we would need to know which MIB 447 # "owned" the OID since the MIB hash is indexed by OID. This is not an 448 # issue in live code since what is fed to getnext for a fully qualified 449 # name is what is returned. 450 '.1.3.6.1.2.1.2.2.1.5' => {38 => 0, 49 => 4294967295, 501 => 1000000000,}, 451 'IF-MIB::ifHighSpeed' => {38 => 0, 49 => 32000, 501 => 1000,}, 452 }; 453 my $expected = {38 => 0, 49 => '32 Gbps', 501 => '1.0 Gbps',}; 454 my $expected_raw = {38 => 0, 49 => 32000000000, 501 => 1000000000,}; 455 $test->{info}{sess}{Data} = $data; 456 cmp_deeply($test->{info}->i_speed_raw(), 457 $expected_raw, 'Raw high speed interface reported accurately'); 458 459 # Note the cache is populated unmunged data now - not sure if that is 460 # expected behavior. Clear cache to get data to test that munges are restored. 461 $test->{info}->clear_cache(); 462 cmp_deeply($test->{info}->i_speed(), 463 $expected, 'Munges restored after i_speed_raw() call'); 464} 465 466sub ip_index : Tests(4) { 467 my $test = shift; 468 469 can_ok($test->{info}, 'ip_index'); 470 471 my $cache_data = { 472 '_old_ip_index' => 1, 473 '_new_ip_index' => 1, 474 '_new_ip_type' => 1, 475 'store' => { 476 'old_ip_index' => 477 {'2.3.4.5' => 7, '2.2.2.2' => 11}, 478 'new_ip_index' => 479 {'1.4.1.2.3.4' => 6, '1.4.10.255.255.255' => 8, '1.4.8.8.8.8' => 10}, 480 'new_ip_type' => 481 {'1.4.1.2.3.4' => 'unicast', '1.4.10.255.255.255' => 'broadcast', '1.4.8.8.8.8' => 'unicast'}, 482 } 483 }; 484 $test->{info}->cache($cache_data); 485 486 my $expected = {'2.3.4.5' => 7, '2.2.2.2' => 11}; 487 488 cmp_deeply($test->{info}->ip_index(), 489 $expected, q(IP addresses mapped to 'ifIndex' using old 'ipAddrTable')); 490 491 delete $test->{info}{_old_ip_index}; 492 $expected = {'1.2.3.4' => 6, '8.8.8.8' => 10}; 493 494 cmp_deeply($test->{info}->ip_index(), 495 $expected, q(IP addresses mapped to 'ifIndex' using new 'ipAddressTable')); 496 497 $test->{info}->clear_cache(); 498 cmp_deeply($test->{info}->ip_index(), {}, q(No data returns empty hash)); 499} 500 501sub ip_table : Tests(4) { 502 my $test = shift; 503 504 can_ok($test->{info}, 'ip_table'); 505 506 my $cache_data = { 507 '_old_ip_table' => 1, 508 '_new_ip_index' => 1, 509 '_new_ip_type' => 1, 510 'store' => { 511 'old_ip_table' => 512 {'2.3.4.5' => '2.3.4.5', '2.2.2.2' => '2.2.2.2'}, 513 'new_ip_index' => 514 {'1.4.1.2.3.4' => 6, '1.4.10.255.255.255' => 8, '1.4.8.8.8.8' => 10}, 515 'new_ip_type' => 516 {'1.4.1.2.3.4' => 'unicast', '1.4.10.255.255.255' => 'broadcast', '1.4.8.8.8.8' => 'unicast'}, 517 } 518 }; 519 $test->{info}->cache($cache_data); 520 521 my $expected = {'2.3.4.5' => '2.3.4.5', '2.2.2.2' => '2.2.2.2'}; 522 523 cmp_deeply($test->{info}->ip_table(), 524 $expected, q(IP addresses using old 'ipAddrTable')); 525 526 delete $test->{info}{_old_ip_table}; 527 $expected = {'1.2.3.4' => '1.2.3.4', '8.8.8.8' => '8.8.8.8'}; 528 529 cmp_deeply($test->{info}->ip_table(), 530 $expected, q(IP addresses using new 'ipAddressTable')); 531 532 $test->{info}->clear_cache(); 533 cmp_deeply($test->{info}->ip_table(), {}, q(No data returns empty hash)); 534} 535 536sub ip_netmask : Tests(4) { 537 my $test = shift; 538 539 can_ok($test->{info}, 'ip_netmask'); 540 541 my $cache_data = { 542 '_old_ip_netmask' => 1, 543 '_new_ip_prefix' => 1, 544 '_new_ip_type' => 1, 545 'store' => { 546 'old_ip_netmask' => 547 {'2.3.4.5' => '255.255.255.0', '2.2.2.2' => '255.255.0.0'}, 548 'new_ip_prefix' => 549 {'1.4.1.2.3.4' => 'IP-MIB::ipAddressPrefixOrigin.2.ipv4."1.2.3.0".24', '1.4.10.2.3.4' => '.1.3.6.1.2.1.4.32.1.5.6.1.4.10.0.0.0.8', '1.4.8.8.8.8' => '.0.0'}, 550 'new_ip_type' => 551 {'1.4.1.2.3.4' => 'unicast', '1.4.10.2.3.4' => 'unicast', '1.4.8.8.8.8' => 'unicast'}, 552 } 553 }; 554 $test->{info}->cache($cache_data); 555 556 my $expected = {'2.3.4.5' => '255.255.255.0', '2.2.2.2' => '255.255.0.0'}; 557 558 cmp_deeply($test->{info}->ip_netmask(), 559 $expected, q(IP netmask using old 'ipAddrTable')); 560 561 delete $test->{info}{_old_ip_netmask}; 562 $expected = {'1.2.3.4' => '255.255.255.0', '10.2.3.4' => '255.0.0.0'}; 563 564 cmp_deeply($test->{info}->ip_netmask(), 565 $expected, q(IP netmask using new 'ipAddressTable')); 566 567 $test->{info}->clear_cache(); 568 cmp_deeply($test->{info}->ip_netmask(), {}, q(No data returns empty hash)); 569} 570 571# Topo routines will need to be tested in sub classes for conditionals 572sub has_topo : Tests(2) { 573 my $test = shift; 574 575 can_ok($test->{info}, 'has_topo'); 576 is($test->{info}->has_topo(), undef, 'Base class has no topo'); 577} 578 579sub get_topo_data : Tests(2) { 580 my $test = shift; 581 582 can_ok($test->{info}, '_get_topo_data'); 583 is($test->{info}->_get_topo_data(), undef, 'Base class has no topo data'); 584} 585 586sub c_ip : Tests(2) { 587 my $test = shift; 588 589 can_ok($test->{info}, 'c_ip'); 590 is($test->{info}->c_ip(), undef, 'Base class has no topo'); 591} 592 593sub c_if : Tests(2) { 594 my $test = shift; 595 596 can_ok($test->{info}, 'c_if'); 597 is($test->{info}->c_if(), undef, 'Base class has no topo'); 598} 599 600sub c_port : Tests(2) { 601 my $test = shift; 602 603 can_ok($test->{info}, 'c_port'); 604 is($test->{info}->c_port(), undef, 'Base class has no topo'); 605} 606 607sub c_id : Tests(2) { 608 my $test = shift; 609 610 can_ok($test->{info}, 'c_id'); 611 is($test->{info}->c_id(), undef, 'Base class has no topo'); 612} 613 614sub c_platform : Tests(2) { 615 my $test = shift; 616 617 can_ok($test->{info}, 'c_platform'); 618 is($test->{info}->c_platform(), undef, 'Base class has no topo'); 619} 620 621sub c_cap : Tests(2) { 622 my $test = shift; 623 624 can_ok($test->{info}, 'c_cap'); 625 is($test->{info}->c_cap(), undef, 'Base class has no topo'); 626} 627 628# Munges aren't methods, the are functions so calling convention is different 629sub munge_speed : Tests(2) { 630 my $test = shift; 631 632 can_ok($test->{info}, 'munge_speed'); 633 is(SNMP::Info::munge_speed('2488000000'), 634 'OC-48', 'Speed munged according to map'); 635} 636 637sub munge_highspeed : Tests(6) { 638 my $test = shift; 639 640 can_ok($test->{info}, 'munge_highspeed'); 641 is(SNMP::Info::munge_highspeed('15000000'), '15 Tbps', 'Tbps munge'); 642 is(SNMP::Info::munge_highspeed('1500000'), 643 '1.5 Tbps', 'Fractional Tbps munge'); 644 is(SNMP::Info::munge_highspeed('15000'), '15 Gbps', 'Gbps munge'); 645 is(SNMP::Info::munge_highspeed('1500'), '1.5 Gbps', 'Fractional Gbps munge'); 646 is(SNMP::Info::munge_highspeed('100'), '100 Mbps', 'Mbps munge'); 647} 648 649sub munge_ip : Tests(2) { 650 my $test = shift; 651 652 can_ok($test->{info}, 'munge_ip'); 653 my $test_ip = pack("C4", split /\./, "123.4.5.6"); 654 is(SNMP::Info::munge_ip($test_ip), 655 "123.4.5.6", 'Binary IP to dotted ASCII munge'); 656} 657 658sub munge_mac : Tests(4) { 659 my $test = shift; 660 661 can_ok($test->{info}, 'munge_mac'); 662 663 # This is how these MACs look in a snmpwalk 664 my $test_mac = "01 23 45 67 89 AB"; 665 my $long_test_mac = "01 23 45 67 89 AB CD"; 666 my $short_test_mac = "01 23 45 67 89"; 667 668 # However, they come across the wire as octet string, so we need to pack 669 # them. Before packing, we need to remove the whitespace 670 foreach ($test_mac, $long_test_mac, $short_test_mac) { 671 $_ =~ s/\s//g; 672 $_ = pack("H*", $_); 673 } 674 675 is(SNMP::Info::munge_mac($test_mac), 676 "01:23:45:67:89:ab", 'Octet string to colon separated ASCII hex string'); 677 is(SNMP::Info::munge_mac($long_test_mac), 678 undef, 'Too long of an octet string returns undef'); 679 is(SNMP::Info::munge_mac($short_test_mac), 680 undef, 'Too short of an octet string returns undef'); 681} 682 683sub munge_prio_mac : Tests(4) { 684 my $test = shift; 685 686 can_ok($test->{info}, 'munge_prio_mac'); 687 688 # This is how these look in a snmpwalk 689 my $test_mac = "01 23 45 67 89 AB CD EF"; 690 my $long_test_mac = "01 23 45 67 89 AB CD EF 02"; 691 my $short_test_mac = "01 23 45 67 89"; 692 693 # However, they come across the wire as octet string, so we need to pack 694 # them. Before packing, we need to remove the whitespace 695 foreach ($test_mac, $long_test_mac, $short_test_mac) { 696 $_ =~ s/\s//g; 697 $_ = pack("H*", $_); 698 } 699 700 is(SNMP::Info::munge_prio_mac($test_mac), 701 "01:23:45:67:89:ab:cd:ef", 702 'Octet string to colon separated ASCII hex string'); 703 is(SNMP::Info::munge_prio_mac($long_test_mac), 704 undef, 'Too long of an octet string returns undef'); 705 is(SNMP::Info::munge_mac($short_test_mac), 706 undef, 'Too short of an octet string returns undef'); 707} 708 709sub munge_prio_port : Tests(4) { 710 my $test = shift; 711 712 can_ok($test->{info}, 'munge_prio_port'); 713 714 # This is how these look in a snmpwalk 715 my $test_mac = "AB CD"; 716 my $long_test_mac = "AB CD EF"; 717 my $short_test_mac = "AB"; 718 719 # However, they come across the wire as octet string, so we need to pack 720 # them. Before packing, we need to remove the whitespace 721 foreach ($test_mac, $long_test_mac, $short_test_mac) { 722 $_ =~ s/\s//g; 723 $_ = pack("H*", $_); 724 } 725 726 is(SNMP::Info::munge_prio_port($test_mac), 727 "ab:cd", 'Octet string to colon separated ASCII hex string'); 728 is(SNMP::Info::munge_prio_port($long_test_mac), 729 undef, 'Too long of an octet string returns undef'); 730 is(SNMP::Info::munge_prio_port($short_test_mac), 731 undef, 'Too short of an string returns undef'); 732} 733 734# Can't see where this code is actually used, remove? 735sub munge_octet2hex : Tests(2) { 736 my $test = shift; 737 738 can_ok($test->{info}, 'munge_octet2hex'); 739 740 # This is how this looks in a snmpwalk 741 my $test_mac = "AB CD"; 742 743 # However, is comes across the wire as octet string, so we need to pack 744 # it. Before packing, we need to remove the whitespace 745 $test_mac =~ s/\s//g; 746 $test_mac = pack("H*", $test_mac); 747 748 is(SNMP::Info::munge_octet2hex($test_mac), 749 "abcd", 'Octet string to ASCII hex string'); 750} 751 752sub munge_dec2bin : Tests(2) { 753 my $test = shift; 754 755 can_ok($test->{info}, 'munge_dec2bin'); 756 757 # This is layers munge, use L3 test case 758 is(SNMP::Info::munge_dec2bin(4), '00000100', 'Binary char to ASCII binary'); 759} 760 761sub munge_bits : Tests(2) { 762 my $test = shift; 763 764 can_ok($test->{info}, 'munge_bits'); 765 766 my $bits = pack("B*", '00010110'); 767 768 is(SNMP::Info::munge_bits($bits), 769 '00010110', 'SNMP2 BITS field to ASCII bit string'); 770} 771 772sub munge_counter64 : Tests(4) { 773 my $test = shift; 774 775 my $hc_octets = 744002524365; 776 777 can_ok($test->{info}, 'munge_counter64'); 778 is(SNMP::Info::munge_counter64(), undef, 'No arg returns undef'); 779 780 # Default is no BigInt 781 is(SNMP::Info::munge_counter64(744002524365), 782 744002524365, 'No BIGINT returns counter'); 783 784SKIP: { 785 eval { 786 require Math::BigInt; 787 1; 788 } or do { 789 skip "Math::BigInt not installed", 1; 790 }; 791 792 my $class = $test->class; 793 my $sess = $test->mock_session; 794 my $big_int_info 795 = $class->new('AutoSpecify' => 0, 'BigInt' => 1, 'Session' => $sess,); 796 797 my $obj = SNMP::Info::munge_counter64(744002524365); 798 isa_ok($obj, 'Math::BigInt', 'Test counter64'); 799 800 } 801} 802 803sub munge_i_up : Tests(4) { 804 my $test = shift; 805 806 can_ok($test->{info}, 'munge_i_up'); 807 808 is(SNMP::Info::munge_i_up(), undef, 'No arg returns undef'); 809 is(SNMP::Info::munge_i_up(4), 'unknown', 'Unknown status'); 810 is(SNMP::Info::munge_i_up(7), 'lowerLayerDown', 'Lower layer down status'); 811} 812 813sub munge_port_list : Tests(6) { 814 my $test = shift; 815 816 can_ok($test->{info}, 'munge_port_list'); 817 818 # Start with the bit string since in a portlist each port is represented as 819 # a bit. 820 # These are typically longer bit strings to cover the all ports in a switch 821 my $bit_string = '01010101010101010101010101010101'; 822 my $bits_packed = pack("B*", $bit_string); 823 824 # This is more for documentation than test code. When performing a snmpwalk 825 # the output will typically be a hex string. This converts the packed bit 826 # string above into a hex string as would be seen in the snmpwalk output. 827 my $unpacked_hex_string 828 = join(' ', map { sprintf "%02X", $_ } unpack('C*', $bits_packed)); 829 830 # This should be the same as $unpacked_hex_string 831 my $hex_string = '55 55 55 55'; 832 833 # Remove the spaces so we can pack, but preserve $hex_string for comparison 834 # testing with $unpacked_hex_string 835 (my $new_hex_string = $hex_string) =~ s/\s//g; 836 837 # Pack the hex string for comparison with $bits_packed 838 my $new_hex_string_packed = pack("H*", $new_hex_string); 839 840 # Finally unpack again to compare with original $bit_string 841 my $new_bit_string = unpack("B*", $new_hex_string_packed); 842 843 # String comparison testing 844 is($unpacked_hex_string, $hex_string, 845 'Unpacking binary bits into hex string is same as expected hex string'); 846 is($new_hex_string_packed, $bits_packed, 847 'Packed hex string is equivalent to packed bit string'); 848 is($new_bit_string, $bit_string, 849 'Unpacking packed hex string as binary results in original bit string'); 850 851 # We are going to get a reference of array of bits back so convert the 852 # string to an array 853 my $expected = []; 854 for my $value (split //, $bit_string) { 855 $expected->[++$#$expected] = $value; 856 } 857 cmp_deeply(SNMP::Info::munge_port_list($bits_packed), 858 $expected, 'Portlist packed bit string coverted to ASCII bit array'); 859 cmp_deeply(SNMP::Info::munge_port_list($new_hex_string_packed), 860 $expected, 'Portlist packed hex string coverted to ASCII bit array'); 861} 862 863sub munge_null : Tests(2) { 864 my $test = shift; 865 866 can_ok($test->{info}, 'munge_null'); 867 868 # See if all possible control characters and nulls are removed 869 my $cntl_string = "Test\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"; 870 $cntl_string .= "This\x0A\x0B\x0C\x0D\x0E\x0F"; 871 $cntl_string .= "Crazy\x11\x12\x13\x14\x15\x16\x17\x18\x19"; 872 $cntl_string .= "Cntl\x1A\x1B\x1C\x1D\x1E\x1F"; 873 $cntl_string .= "\x7FString"; 874 875 # This is layers munge, use L3 test case 876 is(SNMP::Info::munge_null($cntl_string), 877 'TestThisCrazyCntlString', 'Null and control characters removed'); 878} 879 880sub munge_e_type : Tests(3) { 881 my $test = shift; 882 883 can_ok($test->{info}, 'munge_e_type'); 884 885 # This is just calling SNMP::translateOb, so rather than loading another MIB 886 # let's just resolve an OID we don't use from a loaded MIB. 887 is(SNMP::Info::munge_e_type('.1.3.6.1.2.1.11.4'), 888 'snmpInBadCommunityNames', 'OID translated properly'); 889 890 # Bogus OID 891 is(SNMP::Info::munge_e_type('.100.3.6.1.2.1.11.4'), 892 '.100.3.6.1.2.1.11.4', 'OID returned when unable to translate'); 893} 894 895sub resolve_desthost : Tests(6) { 896 my $test = shift; 897 898 can_ok($test->{info}, 'resolve_desthost'); 899 900 is(SNMP::Info::resolve_desthost('1.2.3.4'), 901 '1.2.3.4', 'IPv4 address returns unchanged'); 902 903 is(SNMP::Info::resolve_desthost('::1.2.3.4'), 904 'udp6:0:0:0:0:0:0:102:304', q(IPv6 address returns with 'udp6:' prefix)); 905 906 is( 907 SNMP::Info::resolve_desthost('udp6:fe80::2d0:b7ff:fe21:c6c0'), 908 'udp6:fe80:0:0:0:2d0:b7ff:fe21:c6c0', 909 q(Net-SNMP example with 'udp6:' prefix returns expected string) 910 ); 911 912 is( 913 SNMP::Info::resolve_desthost('fe80::2d0:b7ff:fe21:c6c0'), 914 'udp6:fe80:0:0:0:2d0:b7ff:fe21:c6c0', 915 q(Net-SNMP example IPv6 address returns with 'udp6:' prefix) 916 ); 917 918 dies_ok { SNMP::Info::resolve_desthost('1.2.3.4.5') } 'Bad IP dies'; 919} 920 921sub init : Tests(3) { 922 my $test = shift; 923 924 can_ok($test->{info}, 'init'); 925 926 # When the test info object was created init() was called so all of the 927 # entries in %MIBS should be loaded 928 subtest 'Base MIBs loaded subtest' => sub { 929 930 my $base_mibs = $test->{info}->mibs(); 931 932 foreach my $key (keys %$base_mibs) { 933 my $qual_name = "$key" . '::' . "$base_mibs->{$key}"; 934 ok(defined $SNMP::MIB{$base_mibs->{$key}}, "$qual_name defined"); 935 like(SNMP::translateObj($qual_name), 936 qr/^(\.\d+)+$/, "$qual_name translates to a OID"); 937 } 938 }; 939 940 # Get SNMP::Version so we can restore 941 my $netsnmp_ver = $SNMP::VERSION; 942 local $SNMP::VERSION = '5.0.1'; 943 944 warnings_exist { $test->{info}->init() } 945 [{carped => qr/Net-SNMP\s5.0.1\sseems\sto\sbe\srather\sbuggy/x}], 946 'Use of bad Net-SNMP gives warning'; 947 948 $SNMP::VERSION = $netsnmp_ver; 949} 950 951sub args : Tests(2) { 952 my $test = shift; 953 954 # Match args passed to new() in My::Test::Class 955 my $sess = $test->mock_session; 956 my $args = { 957 'AutoSpecify' => 0, 958 'BulkWalk' => 0, 959 'UseEnums' => 1, 960 'RetryNoSuch' => 1, 961 'DestHost' => '127.0.0.1', 962 'Community' => 'public', 963 'Version' => 2, 964 'Session' => $sess, 965 'Debug' => ($ENV{INFO_TRACE} || 0), 966 'DebugSNMP' => ($ENV{SNMP_TRACE} || 0), 967 }; 968 969 can_ok($test->{info}, 'args'); 970 cmp_deeply($test->{info}->args(), 971 $args, 'Args returned match those passed to new()'); 972} 973 974# Rename this test to prevent conflicts/recursion within test class 975sub class_call : Tests(2) { 976 my $test = shift; 977 my $class = $test->class; 978 979 can_ok($test->{info}, 'class'); 980 is($test->{info}->class(), $class, 'Class method returns object class'); 981} 982 983sub error_throw : Tests(7) { 984 my $test = shift; 985 986 my $error_str = "Test Error String\n"; 987 988 can_ok($test->{info}, 'error_throw'); 989 990 is($test->{info}->error_throw(), undef, 'No error provided returns undef'); 991 is($test->{info}->error(), undef, '... and no error()'); 992 is($test->{info}->error_throw($error_str), 993 undef, 'Error provided returns undef'); 994 995 # Since we don't call with no_clear flag the error is cleared 996 is( 997 $test->{info}->error(), 998 "Test Error String\n", 999 '... and error() returns error string of call' 1000 ); 1001 1002 # Turn on debug to check carp of error 1003 $test->{info}->debug(1); 1004 warning_is { $test->{info}->error_throw($error_str) } 1005 [{carped => 'Test Error String'}], 'Error carped when debug turned on'; 1006 $test->{info}->debug(0); 1007 is( 1008 $test->{info}->error(), 1009 "Test Error String\n", 1010 '... and error() returns error string of call' 1011 ); 1012} 1013 1014sub nosuch : Tests(2) { 1015 my $test = shift; 1016 1017 can_ok($test->{info}, 'nosuch'); 1018 is($test->{info}->nosuch(), 1, 'RetryNoSuch on by default'); 1019} 1020 1021sub session : Tests(4) { 1022 my $test = shift; 1023 my $sess = $test->mock_session; 1024 1025 can_ok($test->{info}, 'session'); 1026 cmp_deeply($test->{info}->session(), $sess, 'Session returned'); 1027 1028 # This will not be a mocked_session so object type and session will be 1029 # different 1030 my $new_sess = SNMP::Session->new( 1031 DestHost => '127.0.0.1', 1032 Community => 'new_public', 1033 Version => 2, 1034 ); 1035 cmp_deeply($test->{info}->session($new_sess), 1036 $new_sess, 'New session returned'); 1037 isa_ok($test->{info}->session(), 'SNMP::Session', 'New session object'); 1038} 1039 1040 1041sub store : Tests(4) { 1042 my $test = shift; 1043 1044 # The store method itself doesn't enforce the naming, so we'll test 1045 # with some totally made up data for 2nd attribute 1046 my $store_data = { 1047 'i_description' => 1048 {10 => 'Test-Description-10', 20 => 'Test-Description-20'}, 1049 'test_attribute' => {Key1 => 'Value 1', Key2 => 'Value 2'} 1050 }; 1051 1052 can_ok($test->{info}, 'store'); 1053 1054 cmp_deeply($test->{info}->store(), {}, 'Store starts empty'); 1055 ok($test->{info}->store($store_data), 'Insert test data into store'); 1056 cmp_deeply( 1057 $store_data, 1058 $test->{info}->store(), 1059 'Store method returns test data' 1060 ); 1061} 1062 1063sub private_global : Tests(14) { 1064 my $test = shift; 1065 1066 can_ok($test->{info}, '_global'); 1067 1068 # This private method and dynamic creation of global methods is covered in 1069 # can() tests. Use these tests to exercise code path for the load_, orig_, 1070 # _raw, as well as, 'NOSUCHOBJECT' and 'NOSUCHINSTANCE' returns so the method 1071 # calls will be indirect. 1072 1073 # Some of these are defined in both SNMPv2-MIB and RFC1213-MIB so use 1074 # OIDs to to make sure no issues with which one was loaded during tests 1075 # Data for load_ 1076 my $data = { 1077 1078 # SNMPv2-MIB::sysContact OID = .1.3.6.1.2.1.1.4 1079 '.1.3.6.1.2.1.1.4' => {0 => 'NOSUCHOBJECT'}, 1080 1081 # SNMPv2-MIB::sysName OID = .1.3.6.1.2.1.1.5 1082 '.1.3.6.1.2.1.1.5' => {0 => 'NOSUCHINSTANCE'}, 1083 1084 # We'll use this to check _raw 1085 # SNMPv2-MIB::sysServices OID = .1.3.6.1.2.1.1.7 1086 '.1.3.6.1.2.1.1.7' => {0 => 64}, 1087 1088 # This is a leaf that we don't reference in %GLOBALS 1089 # SNMPv2-MIB::snmpOutTraps OID = .1.3.6.1.2.1.11.29 1090 '.1.3.6.1.2.1.11.29' => {0 => 245}, 1091 }; 1092 1093 # Lets load cache with data to for initial tests 1094 my $cache_data = {'_layers' => 4, '_name' => 'CacheTestName',}; 1095 1096 # Cache expected after running tests 1097 my $expected_cache = { 1098 '_layers' => 64, 1099 '_snmpOutTraps' => 245, 1100 '_contact' => undef, 1101 '_name' => undef, 1102 'store' => {}, 1103 }; 1104 1105 # Load the data for use in the mock session 1106 $test->{info}{sess}{Data} = $data; 1107 1108 # Load the cache 1109 $test->{info}->cache($cache_data); 1110 1111 is($test->{info}->name(), 'CacheTestName', 1112 'Call to name() loads cached data'); 1113 is($test->{info}->layers(), 1114 '00000100', 'Call to layers() loads cached data and munges'); 1115 is($test->{info}->layers_raw(), 1116 4, 'Call to layers_raw() loads cached data without munge'); 1117 is($test->{info}->load_layers(), 1118 '01000000', 'Call to load_layers loads new data and munges'); 1119 is($test->{info}->layers_raw(), 1120 64, 'Call to layers_raw() loads new data without munge'); 1121 is($test->{info}->snmpOutTraps(), 1122 245, 'Call to snmpOutTraps() resolves MIB leaf and returns data'); 1123 1124 is($test->{info}->load_contact(), 1125 undef, 'Call to load_contact() returns undef'); 1126 is( 1127 $test->{info}->error(), 1128 'SNMP::Info::_global(load_contact) NOSUCHOBJECT', 1129 '... and throws error indicating NOSUCHOBJECT' 1130 ); 1131 is($test->{info}->load_name(), undef, 'Call to load_name() returns undef'); 1132 is( 1133 $test->{info}->error(), 1134 'SNMP::Info::_global(load_name) NOSUCHINSTANCE', 1135 '... and throws error indicating NOSUCHINSTANCE' 1136 ); 1137 cmp_deeply($test->{info}->cache(), 1138 $expected_cache, 'Cache contains expected data'); 1139 1140 # Simulate session error, i.e. get fails 1141 $test->{info}{sess}{ErrorStr} = 'Get Failed'; 1142 1143 # We need to force load to make it to error 1144 is($test->{info}->load_name(), undef, 'Upon session error returned undef'); 1145 is( 1146 $test->{info}->error(), 1147 'SNMP::Info::_global(load_name) Get Failed', 1148 '... and error was thrown' 1149 ); 1150 1151 # Clear error or will impact future tests 1152 $test->{info}{sess}{ErrorStr} = undef; 1153} 1154 1155sub private_set : Tests(12) { 1156 my $test = shift; 1157 1158 can_ok($test->{info}, '_set'); 1159 1160 # Load cache with data so we can check that _set clears 1161 my $cache_data = {'_name' => 'CacheTestName',}; 1162 1163 $test->{info}->cache($cache_data); 1164 is($test->{info}{_name}, 'CacheTestName', 'Cache has a name'); 1165 1166 # Simple set 1167 is($test->{info}->_set('name', 'TestName', 0), 1168 1, 'Valid non-array ref name _set() returned 1'); 1169 is($test->{info}{_name}, undef, '... and now cache is cleared'); 1170 1171 # 4 element array 1172 my $arg_array = ['name', 'TestName', 0]; 1173 is($test->{info}->_set($arg_array), 1174 1, 'Valid array reference name _set() returned 1'); 1175 1176 # Reference to an array of 4 element arrays, also see set_multi 1177 my $arg_aoa = [['name', 'TestName', 0]]; 1178 is($test->{info}->_set($arg_aoa), 1179 1, 'Valid array of arrays reference name _set() returned 1'); 1180 1181 # Bogus args 1182 my $bogus_args = {'name' => 'TestName'}; 1183 is($test->{info}->_set($bogus_args), undef, 'Invalid args returned undef'); 1184 like( 1185 $test->{info}->error(), 1186 qr/SNMP::Info::_set.+-\sFailed/x, 1187 '... and error was thrown' 1188 ); 1189 1190 # Bogus attr 1191 is($test->{info}->_set('no_name', 'TestName', 0), 1192 undef, 'Invalid attr returned undef'); 1193 like( 1194 $test->{info}->error(), 1195 qr/SNMP::Info::_set.+-\sFailed\sto\sfind/x, 1196 '... and error was thrown' 1197 ); 1198 1199 # Simulate session error, i.e. set fails 1200 $test->{info}{sess}{ErrorStr} = 'Set Failed'; 1201 is($test->{info}->_set('name', 'TestName', 0), 1202 undef, 'Upon session error returned undef'); 1203 is( 1204 $test->{info}->error(), 1205 'SNMP::Info::_set Set Failed', 1206 '... and error was thrown' 1207 ); 1208 1209 # Clear error or will impact future tests 1210 $test->{info}{sess}{ErrorStr} = undef; 1211} 1212 1213sub private_make_setter : Tests(10) { 1214 my $test = shift; 1215 1216 # This private method is covered in other tests 1217 can_ok($test->{info}, '_make_setter'); 1218 1219 # This private method and dynamic creation of methods is covered in 1220 # can() tests. Use these tests to exercise code path for non-multi SNMP sets 1221 # so the method calls will be indirect. This will indirectly exercise the 1222 # AUTOLOAD and can methods as well. 1223 1224 # Load cache with data so we can check that _set clears 1225 my $cache_data = {'_name' => 'CacheTestName',}; 1226 1227 $test->{info}->cache($cache_data); 1228 is($test->{info}{_name}, 'CacheTestName', 'Cache has a name'); 1229 1230 # Set on %GLOBALS entry name 1231 is($test->{info}->set_name('TestName'), 1232 1, 'SNMP set on global name with no iid returned 1'); 1233 is($test->{info}{_name}, undef, '... and now cache is cleared'); 1234 1235 # Same set on the MIB leaf 1236 is($test->{info}->set_sysName('TestName'), 1237 1, 'SNMP set on MIB leaf sysName with no iid returned 1'); 1238 1239 # Can provide IID to global if wanted 1240 is($test->{info}->set_name('TestName', 0), 1241 1, 'SNMP set on global name with iid returned 1'); 1242 1243 # Set on a %FUNCS table method 1244 is($test->{info}->set_i_alias('TestPortName', 3), 1245 1, 'SNMP set on func i_description with iid returned 1'); 1246 1247 # Same set on the table MIB leaf 1248 is($test->{info}->set_ifAlias('TestPortName', 3), 1249 1, 'SNMP set on MIB leaf ifAlias with iid returned 1'); 1250 1251 # Simulate session error, i.e. set fails 1252 $test->{info}{sess}{ErrorStr} = 'Set Failed'; 1253 is($test->{info}->set_i_alias('TestPortName', 3), 1254 undef, 'Upon session error returned undef'); 1255 is( 1256 $test->{info}->error(), 1257 'SNMP::Info::_set Set Failed', 1258 '... and error was thrown' 1259 ); 1260 1261 # Clear error or will impact future tests 1262 $test->{info}{sess}{ErrorStr} = undef; 1263} 1264 1265sub set_multi : Tests(3) { 1266 my $test = shift; 1267 1268 can_ok($test->{info}, 'set_multi'); 1269 1270 # This is contrived and mock set always returns true, so this test case 1271 # could be improved. The multi_set method is meant for use similar to a 1272 # database transaction where all sets must be completed in one atomic operation 1273 my $multi_set = [ 1274 ['i_up_admin', 2, 3], 1275 ['i_description', 'Port Down', 3], 1276 ['ifDescr', 'Test Descr', 4], 1277 ]; 1278 1279 is($test->{info}->set_multi($multi_set), 1, 'Valid multi_set() returned 1'); 1280 1281 $multi_set = [ 1282 ['i_up_admin', 2, 3], 1283 ['i_description', 'Port Down', 3], 1284 ['bogus_set', 'What?', 4], 1285 ]; 1286 1287 is($test->{info}->set_multi($multi_set), 1288 undef, 'Invalid multi_set() returned undef'); 1289 1290} 1291 1292sub load_all : Tests(6) { 1293 my $test = shift; 1294 1295 can_ok($test->{info}, 'load_all'); 1296 1297 # This method uses load_ and will not utilize the cache so we need to define 1298 # data for the mocked session's get/getnext methods 1299 1300 # Use OIDs to prevent resolution conflicts of fully qualified names between 1301 # RFC1213-MIB and IF-MIB dependant upon which was loaded first via random 1302 # hash ordering. We only need a subset of data to verify the method is 1303 # working, no need to have data for all funcs the method calls. Use a subset 1304 # of the IF-MIB::ifTable and IF-MIB::ifXTable 1305 my $data = { 1306 '.1.3.6.1.2.1.2.2.1.1' => {1 => 1, 2 => 2, 3 => 3,}, 1307 '.1.3.6.1.2.1.2.2.1.2' => { 1308 1 => 'Loopback', 1309 2 => '1/3/1, 10/100 Ethernet TX', 1310 3 => '1/3/2, 10/100 Ethernet TX' 1311 }, 1312 '.1.3.6.1.2.1.2.2.1.3' => {1 => 24, 2 => 6, 3 => 6}, 1313 '.1.3.6.1.2.1.2.2.1.4' => {1 => 1500, 2 => 1514, 3 => 1514}, 1314 '.1.3.6.1.2.1.2.2.1.5' => {1 => 0, 2 => 100000000, 3 => 1000000000,}, 1315 '.1.3.6.1.2.1.31.1.1.1.15' => {1 => 0, 2 => 100, 3 => 1000,}, 1316 }; 1317 1318 # Data is stored unmunged, OID's will be resolved and cache entries stored 1319 # under %FUNCS names 1320 my $expected_data = { 1321 'i_index' => {1 => 1, 2 => 2, 3 => 3,}, 1322 'i_description' => { 1323 1 => 'Loopback', 1324 2 => '1/3/1, 10/100 Ethernet TX', 1325 3 => '1/3/2, 10/100 Ethernet TX' 1326 }, 1327 'i_type' => {1 => 24, 2 => 6, 3 => 6}, 1328 'i_mtu' => {1 => 1500, 2 => 1514, 3 => 1514}, 1329 'i_speed' => {1 => 0, 2 => 100000000, 3 => 1000000000,}, 1330 'i_speed_high' => {1 => 0, 2 => 100, 3 => 1000,}, 1331 1332 # In base class defined as ifIndex 1333 'interfaces' => {1 => 1, 2 => 2, 3 => 3,}, 1334 }; 1335 1336 # Start with some data in store to verify it is overwritten 1337 my $store_data 1338 = {'i_description' => 1339 {10 => 'Test-Description-10', 20 => 'Test-Description-20'}, 1340 }; 1341 1342 cmp_deeply($test->{info}->store(), {}, 'Store starts empty'); 1343 ok($test->{info}->store($store_data), 'Insert test data into store'); 1344 cmp_deeply($test->{info}->store(), $store_data, 1345 '... store now has test data'); 1346 1347 # Load the data for use in the mock session 1348 $test->{info}{sess}{Data} = $data; 1349 1350 cmp_deeply($test->{info}->load_all(), 1351 $expected_data, 'Call to load_all() returns expected data'); 1352 cmp_deeply($test->{info}->store(), 1353 $expected_data, '... and store now has expected data from load_all()'); 1354} 1355 1356# Need to rename from all to prevent name conflict 1357sub my_all : Tests(9) { 1358 my $test = shift; 1359 1360 can_ok($test->{info}, 'all'); 1361 1362 # Use OIDs to prevent resolution conflicts of fully qualified names between 1363 # RFC1213-MIB and IF-MIB dependant upon which was loaded first via random 1364 # hash ordering. We only need a bare minimum of data to verify the method is 1365 # working since it relies on load_all() which has its own tests. 1366 my $data = { 1367 '.1.3.6.1.2.1.2.2.1.2' => { 1368 1 => 'Loopback', 1369 2 => '1/3/1, 10/100 Ethernet TX', 1370 3 => '1/3/2, 10/100 Ethernet TX' 1371 }, 1372 }; 1373 1374 # Data is stored unmunged, OID's will be resolved and cache entries stored 1375 # under %FUNCS names 1376 my $expected_data = { 1377 'i_description' => { 1378 1 => 'Loopback', 1379 2 => '1/3/1, 10/100 Ethernet TX', 1380 3 => '1/3/2, 10/100 Ethernet TX' 1381 }, 1382 }; 1383 1384 # Start with some data in store to verify it is overwritten on first call 1385 # and whatever is in store() is returned on subsequent calls to all 1386 my $store_data 1387 = {'i_description' => 1388 {10 => 'Test-Description-10', 20 => 'Test-Description-20'}, 1389 }; 1390 1391 cmp_deeply($test->{info}->store(), {}, 'Store starts empty'); 1392 ok($test->{info}->store($store_data), 'Insert test data into store'); 1393 cmp_deeply($test->{info}->store(), $store_data, 1394 '... store now has test data'); 1395 1396 # Load the data for use in the mock session 1397 $test->{info}{sess}{Data} = $data; 1398 1399 cmp_deeply($test->{info}->all(), 1400 $expected_data, 'Call to all() returns expected data'); 1401 cmp_deeply($test->{info}->store(), 1402 $expected_data, '... and store now has expected data from all()'); 1403 1404 ok($test->{info}->store($store_data), 'Re-insert test data into store'); 1405 cmp_deeply($test->{info}->store(), 1406 $store_data, '... store again has test data'); 1407 1408 cmp_deeply($test->{info}->all(), 1409 $expected_data, 1410 '... call to all() returns test data, no call to load_all()'); 1411} 1412 1413sub private_load_attr : Tests(18) { 1414 my $test = shift; 1415 1416 can_ok($test->{info}, '_load_attr'); 1417 1418 # This private method and dynamic creation of table aka func methods is 1419 # covered in can() tests. Use these tests to exercise code path for 1420 # the load_, orig_, _raw, as well as, 'NOSUCHOBJECT', 'NOSUCHINSTANCE', 1421 # and 'ENDOFMIBVIEW' returns so the method calls will be indirect. 1422 1423 # Currently mocked session in test harness doesn't support bulkwalk, so 1424 # that code path is not tested 1425 1426 # Some of these are defined in both SNMPv2-MIB and RFC1213-MIB so use 1427 # OIDs to to make sure no issues with which one was loaded during tests 1428 # Data for load_ 1429 my $data = { 1430 '.1.3.6.1.2.1.2.2.1.2' => { 1431 1 => 'Loopback', 1432 2 => '1/3/1, 10/100 Ethernet TX', 1433 3 => '1/3/2, 10/100 Ethernet TX' 1434 }, 1435 '.1.3.6.1.2.1.2.2.1.8' => {1 => 4, 2 => 'up', 3 => 7}, 1436 'IF-MIB::ifPromiscuousMode' => {1 => 'false', 2 => 'true', 3 => 'false'}, 1437 'IF-MIB::ifConnectorPresent' => {0 => 'NOSUCHOBJECT'}, 1438 'IF-MIB::ifCounterDiscontinuityTime' => {0 => 'NOSUCHINSTANCE'}, 1439 'IF-MIB::ifHCOutOctets' => 1440 {1 => 0, 2 => 1828306359704, 3 => 1002545943585, 4 => 'ENDOFMIBVIEW'}, 1441 1442 # Tables to test partial and full OIDs 1443 '.1.3.6.1.4.1.171.12.1.1.12' => {1 => 'partial', 2 => 'oid', 3 => 'data'}, 1444 '.100.3.6.1.4.1.171.12.1.1.12' => {2 => 'full', 3 => 'oid', 4 => 'leaf'}, 1445 }; 1446 1447 # Load cache with data to for initial tests 1448 my $cache_data = { 1449 '_i_description' => 1, 1450 '_i_up' => 1, 1451 'store' => { 1452 'i_description' => 1453 {10 => 'Test-Description-10', 20 => 'Test-Description-20'}, 1454 'i_up' => {10 => 6, 20 => 7} 1455 } 1456 }; 1457 1458 # Cache expected after running tests 1459 # Note: i_up starts in cache and call to load_i_up for new data increments 1460 # the cache counter 1461 # Note: We don't call load_i_description so the data from cache never gets 1462 # replaced 1463 # Note: munge_i_up only munges integers 4-7 as 1-3 are already enumerated 1464 my $expected_cache = { 1465 '_i_description' => 1, 1466 '_i_up' => 2, 1467 '_ifPromiscuousMode' => 1, 1468 '_ifConnectorPresent' => undef, 1469 '_ifCounterDiscontinuityTime' => undef, 1470 '_i_octet_out64' => 1, 1471 'store' => { 1472 'i_description' => 1473 {10 => 'Test-Description-10', 20 => 'Test-Description-20'}, 1474 'i_up' => {1 => 4, 2 => 'up', 3 => 7}, 1475 'ifPromiscuousMode' => {1 => 'false', 2 => 'true', 3 => 'false'}, 1476 'i_octet_out64' => {1 => 0, 2 => 1828306359704, 3 => 1002545943585} 1477 } 1478 }; 1479 1480 my $expected_cache_munge_iup = {10 => 'notPresent', 20 => 'lowerLayerDown'}; 1481 my $expected_load_munge_iup 1482 = {1 => 'unknown', 2 => 'up', 3 => 'lowerLayerDown'}; 1483 1484 my $expected_load_raw_iftype = {1 => 24, 2 => 6, 3 => 6}; 1485 1486 # Load the data for use in the mock session 1487 $test->{info}{sess}{Data} = $data; 1488 1489 # Load the cache 1490 $test->{info}->cache($cache_data); 1491 1492 cmp_deeply( 1493 $test->{info}->i_description(), 1494 $cache_data->{'store'}{'i_description'}, 1495 'Call to i_description() loads cached data' 1496 ); 1497 cmp_deeply($test->{info}->i_up(), 1498 $expected_cache_munge_iup, 'Call to i_up() loads cached data and munges'); 1499 cmp_deeply( 1500 $test->{info}->i_up_raw(), 1501 $cache_data->{'store'}{'i_up'}, 1502 'Call to i_up_raw() loads cached data without munge' 1503 ); 1504 cmp_deeply($test->{info}->load_i_up(), 1505 $expected_load_munge_iup, 'Call to load_i_up() loads new data and munges'); 1506 cmp_deeply( 1507 $test->{info}->i_up_raw(), 1508 $expected_cache->{'store'}{'i_up'}, 1509 'Call to i_up_raw() loads new data without munge' 1510 ); 1511 1512 # Test ability to use MIB leaf 1513 cmp_deeply( 1514 $test->{info}->ifPromiscuousMode(), 1515 $data->{'IF-MIB::ifPromiscuousMode'}, 1516 'Call to ifPromiscuousMode() resolves MIB leaf and returns data' 1517 ); 1518 1519 # Test error conditions 1520 is($test->{info}->load_ifConnectorPresent(), 1521 undef, 'Call to load_ifConnectorPresent() returns undef'); 1522 is( 1523 $test->{info}->error(), 1524 'SNMP::Info::_load_attr: load_ifConnectorPresent : NOSUCHOBJECT', 1525 '... and throws error indicating NOSUCHOBJECT' 1526 ); 1527 is($test->{info}->load_ifCounterDiscontinuityTime(), 1528 undef, 'Call to load_ifCounterDiscontinuityTime() returns undef'); 1529 is( 1530 $test->{info}->error(), 1531 'SNMP::Info::_load_attr: load_ifCounterDiscontinuityTime : NOSUCHINSTANCE', 1532 '... and throws error indicating NOSUCHINSTANCE' 1533 ); 1534 1535 # 'ENDOFMIBVIEW' isn't an error condition, it just stops the walk 1536 # Ask for raw since don't want munge_counter64 to turn results into objects 1537 # and want to compare to what will be stored in cache at the end 1538 cmp_deeply( 1539 $test->{info}->i_octet_out64_raw(), 1540 $expected_cache->{'store'}{'i_octet_out64'}, 1541 'Call to i_up_raw() loads new data without munge' 1542 ); 1543 1544 # Test partial fetches 1545 cmp_deeply( 1546 $test->{info}->i_octet_out64_raw(3), 1547 +{3 => 1002545943585}, 1548 'Partial call to i_octet_out64_raw(3) data without munge' 1549 ); 1550 cmp_deeply( 1551 $test->{info}->i_description(2), 1552 +{2 => '1/3/1, 10/100 Ethernet TX'}, 1553 'Partial call to i_description(2) loads new data' 1554 ); 1555 ok(!exists $test->{info}{store}{i_description}{2}, 1556 '... and does not store it in cache'); 1557 1558 cmp_deeply($test->{info}->cache(), 1559 $expected_cache, 'Cache contains expected data'); 1560 1561 # Test OID based table fetches 1562 # This is from Layer3::DLink will only partially resolve 1563 $test->{info}{funcs}{partial_oid} = '.1.3.6.1.4.1.171.12.1.1.12'; 1564 1565 my $expected_p_oid_data = {1 => 'partial', 2 => 'oid', 3 => 'data'}; 1566 1567 cmp_deeply($test->{info}->partial_oid(), 1568 $expected_p_oid_data, 'Partial translated OID leaf returns expected data'); 1569 1570 # This is a bogus OID will not translate at all 1571 $test->{info}{funcs}{full_oid} = '.100.3.6.1.4.1.171.12.1.1.12'; 1572 1573 my $expected_f_oid_data = {2 => 'full', 3 => 'oid', 4 => 'leaf'}; 1574 1575 cmp_deeply($test->{info}->full_oid(), 1576 $expected_f_oid_data, 'Full OID leaf returns expected data'); 1577} 1578 1579sub private_show_attr : Tests(3) { 1580 my $test = shift; 1581 1582 can_ok($test->{info}, '_show_attr'); 1583 1584 # Load cache with data 1585 my $cache_data = {'_i_up' => 1, 'store' => {'i_up' => {10 => 6, 20 => 7}}}; 1586 1587 # Load the cache 1588 $test->{info}->cache($cache_data); 1589 1590 my $expected_munge = {10 => 'notPresent', 20 => 'lowerLayerDown'}; 1591 1592 # Minimal tests as this method is heavily covered in other testing 1593 cmp_deeply($test->{info}->_show_attr('i_up'), 1594 $expected_munge, 'Shows munged data from cache without raw flag'); 1595 cmp_deeply( 1596 $test->{info}->_show_attr('i_up', 1), 1597 $cache_data->{'store'}{'i_up'}, 1598 'Shows unmunged data from cache with raw flag' 1599 ); 1600} 1601 1602sub snmp_connect_ip : Tests(3) { 1603 my $test = shift; 1604 1605 can_ok($test->{info}, 'snmp_connect_ip'); 1606 1607 is($test->{info}->snmp_connect_ip('127.0.0.1', 2, 'public'), 1608 undef, 'Connect to loopback returns undef'); 1609 is($test->{info}->snmp_connect_ip('0.0.0.0', 2, 'public'), 1610 undef, 'Connect to zeros returns undef'); 1611 1612 # Live call moved to 10_remote_snmplabs.t 1613} 1614 1615sub modify_port_list : Tests(4) { 1616 my $test = shift; 1617 1618 can_ok($test->{info}, 'modify_port_list'); 1619 1620 # Let munge_port_list do the work of converting this into the portlist array 1621 my $orig_plist = SNMP::Info::munge_port_list(pack("B*", '01010101')); 1622 my $new_plist_1 = pack("B*", '01110101'); 1623 my $new_plist_0 = pack("B*", '01110001'); 1624 my $expanded_plist = pack("B*", '0111000100000001'); 1625 1626 # This call will actually modify $orig_plist 1627 is($test->{info}->modify_port_list($orig_plist, 2, 1), 1628 $new_plist_1, 'Bit in offset position 2 changed to on'); 1629 1630 # Here we start with modified $orig_plist, now '01110101' 1631 is($test->{info}->modify_port_list($orig_plist, 5, 0), 1632 $new_plist_0, 'Bit in offset position 5 changed to off'); 1633 1634 # Modified $orig_plist, now '01110001' 1635 is($test->{info}->modify_port_list($orig_plist, 15, 1), 1636 $expanded_plist, 1637 'Bit in offset position 15 changed to on and portlist array expanded'); 1638} 1639 1640sub private_cache : Tests(1) { 1641 my $test = shift; 1642 1643 # This method is covered in private_global and private_load_attr 1644 # tests so just cover with can() here 1645 can_ok($test->{info}, '_cache'); 1646} 1647 1648sub private_munge : Tests(1) { 1649 my $test = shift; 1650 1651 # This method is covered in private_global and private_load_attr 1652 # tests so just cover with can() here 1653 can_ok($test->{info}, '_munge'); 1654} 1655 1656sub private_validate_autoload_method : Tests(8) { 1657 my $test = shift; 1658 1659 can_ok($test->{info}, '_validate_autoload_method'); 1660 1661 subtest '%GLOBALS _validate_autoload_method() subtest' => sub { 1662 1663 foreach my $prefix ('', 'load_', 'orig_') { 1664 my $test_global = "$prefix" . 'contact'; 1665 cmp_deeply( 1666 $test->{info}->_validate_autoload_method("$test_global"), 1667 ['.1.3.6.1.2.1.1.4.0', 0], 1668 qq(Global '$test_global' validates) 1669 ); 1670 } 1671 cmp_deeply( 1672 $test->{info}->_validate_autoload_method('set_contact'), 1673 ['.1.3.6.1.2.1.1.4', 0], 1674 q(Global 'set_contact' validates) 1675 ); 1676 cmp_deeply( 1677 $test->{info}->_validate_autoload_method('contact_raw'), 1678 ['.1.3.6.1.2.1.1.4.0', 0], 1679 q(Global 'contact_raw' validates) 1680 ); 1681 }; 1682 1683 # Use a leaf we don't have defined in %GLOBALS that is read/write to pass 1684 # all tests, we'll test access separately 1685 subtest 'Single instance MIB leaf _validate_autoload_method() subtest' => 1686 sub { 1687 1688 foreach my $prefix ('', 'load_', 'orig_') { 1689 my $test_global = "$prefix" . 'snmpEnableAuthenTraps'; 1690 cmp_deeply( 1691 $test->{info}->_validate_autoload_method("$test_global"), 1692 ['.1.3.6.1.2.1.11.30.0', 0], 1693 qq(MIB leaf '$test_global' validates) 1694 ); 1695 } 1696 cmp_deeply( 1697 $test->{info}->_validate_autoload_method('set_snmpEnableAuthenTraps'), 1698 ['.1.3.6.1.2.1.11.30', 0], 1699 q(MIB leaf 'set_snmpEnableAuthenTraps' validates) 1700 ); 1701 cmp_deeply( 1702 $test->{info}->_validate_autoload_method('snmpEnableAuthenTraps_raw'), 1703 ['.1.3.6.1.2.1.11.30.0', 0], 1704 q(MIB leaf 'snmpEnableAuthenTraps_raw' validates) 1705 ); 1706 }; 1707 1708 subtest '%FUNCS _validate_autoload_method() subtest' => sub { 1709 1710 foreach my $prefix ('', 'load_', 'orig_') { 1711 my $test_global = "$prefix" . 'i_alias'; 1712 cmp_deeply( 1713 $test->{info}->_validate_autoload_method("$test_global"), 1714 ['.1.3.6.1.2.1.31.1.1.1.18', 1], 1715 qq(Func '$test_global' validates) 1716 ); 1717 } 1718 cmp_deeply( 1719 $test->{info}->_validate_autoload_method('set_i_alias'), 1720 ['.1.3.6.1.2.1.31.1.1.1.18', 1], 1721 q(Func 'set_i_alias' validates) 1722 ); 1723 cmp_deeply( 1724 $test->{info}->_validate_autoload_method('i_alias_raw'), 1725 ['.1.3.6.1.2.1.31.1.1.1.18', 1], 1726 q(Func 'i_alias_raw' validates) 1727 ); 1728 }; 1729 1730 # Use a leaf we don't have defined in %FUNCS that is read/write to pass 1731 # all tests, we'll test access separately 1732 subtest 'Table MIB leaf _validate_autoload_method() subtest' => sub { 1733 1734 foreach my $prefix ('', 'load_', 'orig_') { 1735 my $test_global = "$prefix" . 'ifPromiscuousMode'; 1736 cmp_deeply( 1737 $test->{info}->_validate_autoload_method("$test_global"), 1738 ['.1.3.6.1.2.1.31.1.1.1.16', 1], 1739 qq(Func '$test_global' validates) 1740 ); 1741 } 1742 cmp_deeply( 1743 $test->{info}->_validate_autoload_method('set_ifPromiscuousMode'), 1744 ['.1.3.6.1.2.1.31.1.1.1.16', 1], 1745 q(Func 'set_ifPromiscuousMode' validates) 1746 ); 1747 cmp_deeply( 1748 $test->{info}->_validate_autoload_method('ifPromiscuousMode_raw'), 1749 ['.1.3.6.1.2.1.31.1.1.1.16', 1], 1750 q(Func 'ifPromiscuousMode_raw' validates) 1751 ); 1752 }; 1753 1754 is($test->{info}->_validate_autoload_method('ifStackHigherLayer'), 1755 undef, q(MIB leaf 'ifStackHigherLayer' not-accessible, returns undef)); 1756 1757 # Test that read-only leaf won't validate set_ 1758 is($test->{info}->_validate_autoload_method('set_i_lastchange'), 1759 undef, 1760 q(Func 'i_lastchange' is read-only, 'set_i_lastchange' returns undef)); 1761 1762 # Check fully qualified MIB leaf w substitutions validates 1763 cmp_deeply( 1764 $test->{info}->_validate_autoload_method('IF_MIB__ifConnectorPresent'), 1765 ['.1.3.6.1.2.1.31.1.1.1.17', 1], 1766 q(Fully qualified 'IF_MIB__ifConnectorPresent' validates) 1767 ); 1768} 1769 1770# Prefix with private as we don't want to accidentally override in test class 1771sub private_can : Tests(9) { 1772 my $test = shift; 1773 1774 # This method is heavily covered across tests, just verify here that 1775 # a successful call places the method in the symbol table 1776 1777 # See perldoc Symbol, specifically Symbol::delete_package. We can't assume 1778 # symbols are deleted with the object between tests. Since we can() all 1779 # globals and funcs during tests use MIB leafs to test table and scalar 1780 # methods. The symbol_test() method is defined in My::Test::Class 1781 # Note: if these tests start to fail make sure we aren't using the leaf 1782 # in other tests 1783 1784 # This leaf tests the global path SNMPv2-MIB::snmpInBadCommunityNames 1785 is($test->symbol_test('snmpInBadCommunityNames'), 1786 0, q(Method 'snmpInBadCommunityNames' is not defined in the symbol table)); 1787 can_ok($test->{info}, 'snmpInBadCommunityNames'); 1788 is($test->symbol_test('snmpInBadCommunityNames'), 1789 1, q(Method 'snmpInBadCommunityNames' is now defined in the symbol table)); 1790 1791 # This leaf tests the table path IF-MIB::ifCounterDiscontinuityTime 1792 is($test->symbol_test('ifCounterDiscontinuityTime'), 1793 0, 1794 q(Method 'ifCounterDiscontinuityTime' is not defined in the symbol table)); 1795 can_ok($test->{info}, 'ifCounterDiscontinuityTime'); 1796 is($test->symbol_test('ifCounterDiscontinuityTime'), 1797 1, 1798 q(Method 'ifCounterDiscontinuityTime' is now defined in the symbol table)); 1799 1800 # This leaf tests the set_ path SNMPv2-MIB::snmpSetSerialNo 1801 is($test->symbol_test('set_snmpSetSerialNo'), 1802 0, q(Method 'set_snmpSetSerialNo' is not defined in the symbol table)); 1803 can_ok($test->{info}, 'set_snmpSetSerialNo'); 1804 is($test->symbol_test('set_snmpSetSerialNo'), 1805 1, q(Method 'set_snmpSetSerialNo' is now defined in the symbol table)); 1806} 1807 1808# Prefix with private as we don't want to accidentally override in test class 1809sub private_autoload : Tests(3) { 1810 my $test = shift; 1811 1812 # This method is covered in other tests, just verify here that 1813 # a successful call places the method in the symbol table. Since can() does 1814 # the majority of the work call a method without calling can() first. Same 1815 # as noted in the private_can test we need to use a leaf not used elsewhere 1816 # (IP-MIB::ipDefaultTTL) to make sure method isn't in the symbol table first. 1817 # AUTOLOAD calls the method after inserted in the symbol table, so we 1818 # populate cache to return some data 1819 1820 # Load the cache 1821 my $cache_data = {'_ipDefaultTTL' => 64}; 1822 $test->{info}->cache($cache_data); 1823 1824 is($test->symbol_test('ipDefaultTTL'), 1825 0, q(Method 'ipDefaultTTL' is not defined in the symbol table)); 1826 is($test->{info}->ipDefaultTTL(), 1827 64, q(Method 'ipDefaultTTL' called and returned expected data')); 1828 is($test->symbol_test('ipDefaultTTL'), 1829 1, q(Method 'ipDefaultTTL' is now defined in the symbol table)); 1830} 1831 1832# Prefix with private as we don't want to accidentally override in test class 1833sub private_destroy : Tests(2) { 1834 my $test = shift; 1835 1836 can_ok($test->{info}, 'DESTROY'); 1837 is($test->{info}->DESTROY(), undef, 'DESTROY returns undef'); 1838} 1839 18401; 1841