Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Relevant replacement documents include:
MoreSCF/MoreSCFTest/MoreSCFTest.c
/* |
File: MoreSCFTest.c |
Contains: Test program for all of MoreSCF. |
Written by: DTS |
Copyright: Copyright (c) 2007 by Apple Inc., All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple's |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
$Log: MoreSCFTest.c,v $ |
Revision 1.14 2006/11/24 15:01:58 eskimo1 |
Make it possible to explicitly test the old port scanning code. Fix <rdar://problem/3540864>, which was causing original AirPort Extreme cards to not be recognised as AirPort. |
Revision 1.13 2006/11/24 12:39:35 eskimo1 |
Don't crash if the sort order isn't present. |
Revision 1.12 2006/11/23 15:04:24 eskimo1 |
Quieten an uninitialised variable warning. |
Revision 1.11 2006/03/27 14:42:46 eskimo1 |
Eliminate high-bit set characters. |
Revision 1.10 2006/03/24 15:44:51 eskimo1 |
Updated copyright. |
Revision 1.9 2006/03/24 12:38:59 eskimo1 |
Eliminate "pascal" keyword. |
Revision 1.8 2006/03/24 11:30:25 eskimo1 |
Eliminated "MoreSetup.h" to make it easier for folks to copy MIB source into their projects. |
Revision 1.7 2004/10/28 12:36:27 eskimo1 |
Add TestTrivialCreate to exercise a buggy case discovered by a developer. |
Revision 1.6 2003/03/11 23:22:05 eskimo1 |
Added DebugCreatePPPoE. |
Revision 1.5 2003/02/26 21:02:49 eskimo1 |
Serious rewrite. The tool now behaves differently depending on command line parameters. Also, worked around a bug in the Network prefs panel that was causing the set comparison to fail even though MoreSCF is doing the right thing. |
Revision 1.4 2003/02/26 12:47:05 eskimo1 |
Don't print high-bit set characters because they look ugly when running under Terminal. In the code to compare sets, changed lhs and rhs to new and def (default), so make it easier to understand which is which. Modified TestISP to test the PPPoE code. Added DebugCreateSet. |
Revision 1.3 2002/11/09 00:02:39 eskimo1 |
Convert nil to NULL. Convert MoreAssertQ to assert. |
Revision 1.2 2002/01/22 06:24:13 eskimo1 |
Adapt to the new port scanner API. Also added some extra printfs when we get a comparison failure in TestCreateSet to make it easier to track down problems. |
Revision 1.1 2002/01/16 22:53:11 eskimo1 |
First checked in. |
*/ |
///////////////////////////////////////////////////////////////// |
// System interfaces |
#include <CoreFoundation/CoreFoundation.h> |
#include <stdio.h> |
#include <unistd.h> |
// MIB Interfaces |
#include "MoreCFQ.h" |
#include "MoreSCFPortScanner.h" |
#include "MoreSCFCCLScanner.h" |
#include "MoreSCF.h" |
#include "MoreSCFDigest.h" |
#include "MoreSCFHelpers.h" |
///////////////////////////////////////////////////////////////// |
#pragma mark ***** Tests |
static Boolean gNoRootTests = false; |
// If this is true, we only runs tests root tests if we're running as root. |
static Boolean gRunQuiet = false; |
// When this is set to true the test routines print nothing |
// during successful operation. This prevents a bunch of meaningless |
// output during the memory leak tests. |
static Boolean gCheckLeaks = false; |
// When this is set, tests are run repeatedly to check for leaks. |
static Boolean gCheckLeaksDebug = false; |
// When this is set, the leak testing code prints some helpful debugging. |
static const char *gProgramName; |
// The last path element of argv[0]. Used when printing error messages. |
extern void PrintPropertyList(CFPropertyListRef propList); |
// Forward declaration. Declared external for debugging purposes |
// in other modules. |
static void PrintTestResult(OSStatus errNum, const char *subTestName); |
// Forward declaration of this test engine function. |
extern OSStatus MoreSCCreatePortArrayOld(CFArrayRef *portArray); |
// This is an internal function of the MoreSCFPortScanner that I want to |
// call from the test program but I don't want to generally export. Thus, |
// I declare my own private prototype for the routine. |
static void TestPortScannerCommon(Boolean useOldCode) |
// A simple test of the port scanner code. This doesn't do a |
// lot of automated testing, but you can look at the results |
// and visually check them. |
{ |
OSStatus err; |
CFArrayRef portArray; |
CFIndex portCount; |
CFIndex portIndex; |
long order; |
CFNumberRef supportsHold; |
portArray = NULL; |
if (useOldCode) { |
err = MoreSCCreatePortArrayOld(&portArray); |
} else { |
err = MoreSCCreatePortArray(&portArray); |
} |
if (err == noErr) { |
portCount = CFArrayGetCount(portArray); |
for (portIndex = 0; portIndex < portCount; portIndex++) { |
CFDictionaryRef thisPort; |
thisPort = (CFDictionaryRef) CFArrayGetValueAtIndex(portArray, portIndex); |
if (!gRunQuiet) { |
fprintf(stderr, "Port %ld\n", portIndex); |
fprintf(stderr, " device = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropNetInterfaceDeviceName)); |
fprintf(stderr, " name = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropUserDefinedName)); |
fprintf(stderr, " hardware = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropNetInterfaceHardware)); |
fprintf(stderr, " variant = "); |
CFShow(CFDictionaryGetValue(thisPort, kMoreSCPropNetInterfaceHardwareVariant)); |
fprintf(stderr, " type = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropNetInterfaceType)); |
fprintf(stderr, " subtype = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropNetInterfaceSubType)); |
fprintf(stderr, " MAC = "); |
CFShow(CFDictionaryGetValue(thisPort, kSCPropMACAddress)); |
supportsHold = (CFNumberRef) CFDictionaryGetValue(thisPort, kSCPropNetInterfaceSupportsModemOnHold); |
if (supportsHold != NULL) { |
long hold; |
CFNumberGetValue(supportsHold, kCFNumberLongType, &hold); |
fprintf(stderr, " hold = %ld\n", hold); |
} |
// The "SortOrder" property isn't part of the MoreSCF 'API'. That means a) there's no constant |
// definition for it, and b) it's not always present (specifically, if we're using the new API, |
// which returns the ports sorted, I don't bother trying to emulate the value). However, I do |
// want to print it if it's present, because it's helpful when debugging the old code. |
if ( CFDictionaryContainsKey(thisPort, CFSTR("SortOrder")) ) { |
(void) CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(thisPort, CFSTR("SortOrder")), kCFNumberLongType, &order); |
fprintf(stderr, " sort = %ld\n", order); |
} |
} |
} |
} |
CFQRelease(portArray); |
PrintTestResult(err, NULL); |
} |
static void TestPortScanner(void) |
{ |
TestPortScannerCommon(false); |
} |
static void TestPortScannerOld(void) |
{ |
TestPortScannerCommon(true); |
} |
static void TestCCLScanner(void) |
// A simple test of the port scanner code. This doesn't do a |
// lot of automated testing, but you can look at the results |
// and visually check them. |
{ |
OSStatus err; |
CFArrayRef cclArray; |
CFIndex indexOfDefault; |
CFIndex cclCount; |
CFIndex cclIndex; |
cclArray = NULL; |
err = MoreSCCreateCCLArray(&cclArray, &indexOfDefault); |
if ( (err == noErr) && ! gRunQuiet ) { |
cclCount = CFArrayGetCount(cclArray); |
for (cclIndex = 0; cclIndex < cclCount; cclIndex++) { |
if (cclIndex == indexOfDefault) { |
fprintf(stderr, " * "); |
} else { |
fprintf(stderr, " "); |
} |
CFShow( CFArrayGetValueAtIndex(cclArray, cclIndex) ); |
} |
} |
CFQRelease(cclArray); |
PrintTestResult(err, NULL); |
} |
static void TestCopySetsDict(void) |
// A test of the MoreSCCopySetsDict routine, which proved key |
// in tracking down a reference count leak. |
{ |
OSStatus err; |
CFDictionaryRef setsDict; |
setsDict = NULL; |
err = MoreSCCopySetsDict(&setsDict); |
CFQRelease(setsDict); |
PrintTestResult(err, NULL); |
} |
#define kDefaultLocationName "DefaultLocationForMoreSCFTest" |
static void TestSetEnumerationAndSwitch(void) |
// A test of set enumeration and switching routines. |
{ |
OSStatus err; |
CFIndex setCount; |
CFIndex setIndex; |
CFArrayRef setIDs; |
CFIndex indexOfCurrentSet; |
setCount = 0; // quieten warning |
setIDs = NULL; |
// Enumeration |
err = MoreSCCopySetIDs(&setIDs, &indexOfCurrentSet); |
if (err == noErr) { |
setCount = CFArrayGetCount(setIDs); |
for (setIndex = 0; setIndex < setCount; setIndex++) { |
CFStringRef userVis; |
userVis = NULL; |
err = MoreSCCopyUserVisibleNameOfSet( (CFStringRef) CFArrayGetValueAtIndex(setIDs, setIndex), &userVis); |
if (err == noErr && !gRunQuiet) { |
fprintf(stderr, "#%ld %c ", setIndex, (setIndex == indexOfCurrentSet) ? '*' : ' '); |
CFShow(userVis); |
CFShow(CFArrayGetValueAtIndex(setIDs, setIndex)); |
} |
CFQRelease(userVis); |
if (err != noErr) { |
break; |
} |
} |
} |
if (err == noErr) { |
CFStringRef currentSetID; |
currentSetID = NULL; |
err = MoreSCCopyCurrentSet(¤tSetID); |
if (err == noErr && !gRunQuiet) { |
fprintf(stderr, "Current set ID is "); |
CFShow(currentSetID); |
} |
CFQRelease(currentSetID); |
} |
// Switch |
if (err == noErr) { |
if (setCount == 1) { |
fprintf(stderr, "*** TestSetEnumerationAndSwitch requires that you create a default set named '%s' using the Network preferences panel.\n", kDefaultLocationName); |
err = -1; |
} else { |
ItemCount newSet; |
if (indexOfCurrentSet == 0) { |
newSet = 1; |
} else { |
newSet = 0; |
} |
err = MoreSCSetCurrentSet( (CFStringRef) CFArrayGetValueAtIndex(setIDs, newSet)); |
if (err == noErr) { |
err = MoreSCSetCurrentSet( (CFStringRef) CFArrayGetValueAtIndex(setIDs, indexOfCurrentSet)); |
} |
} |
} |
CFQRelease(setIDs); |
PrintTestResult(err, NULL); |
} |
static void TestDuplicateAndDeleteSet(void) |
// A test of the set duplication and deleting routines. |
{ |
OSStatus err; |
CFStringRef currentSetID; |
CFStringRef newSetID; |
currentSetID = NULL; |
newSetID = NULL; |
err = MoreSCCopyCurrentSet(¤tSetID); |
if (err == noErr) { |
err = MoreSCDuplicateSet(currentSetID, CFSTR("Frog"), &newSetID); |
} |
if (err == noErr) { |
if (!gRunQuiet) { |
fprintf(stderr, "New set ID is "); |
CFShow(newSetID); |
} |
err = MoreSCDeleteSet(newSetID); |
} |
CFQRelease(currentSetID); |
CFQRelease(newSetID); |
PrintTestResult(err, NULL); |
} |
static void TestRenameSet(void) |
// A test of the MoreSCRenameSet routine. |
{ |
OSStatus err; |
CFStringRef currentSetID; |
CFStringRef originalName; |
CFStringRef newName; |
currentSetID = NULL; |
originalName = NULL; |
newName = NULL; |
err = MoreSCCopyCurrentSet(¤tSetID); |
if (err == noErr) { |
err = MoreSCCopyUserVisibleNameOfSet(currentSetID, &originalName); |
} |
if (err == noErr) { |
err = MoreSCRenameSet(currentSetID, CFSTR("Frog")); |
} |
if (err == noErr) { |
err = MoreSCCopyUserVisibleNameOfSet(currentSetID, &newName); |
} |
if (err == noErr) { |
if ( ! CFEqual(newName, CFSTR("Frog")) ) { |
fprintf(stderr, "*** newName isn't 'Frog'\n"); |
CFShow(newName); |
} |
err = MoreSCRenameSet(currentSetID, originalName); |
} |
if (err == noErr) { |
CFQRelease(newName); |
newName = NULL; |
err = MoreSCCopyUserVisibleNameOfSet(currentSetID, &newName); |
} |
if (err == noErr) { |
if ( ! CFEqual(newName, originalName) ) { |
fprintf(stderr, "*** newName isn't the same as originalName\n"); |
CFShow(newName); |
} |
} |
CFQRelease(currentSetID); |
CFQRelease(originalName); |
CFQRelease(newName); |
PrintTestResult(err, NULL); |
} |
static void TestServiceEnumerate(void) |
// A test of the service enumeration routines. |
{ |
OSStatus err; |
ItemCount serviceCount; |
ItemCount serviceIndex; |
CFArrayRef localServiceIDs; |
CFArrayRef resolvedServiceIDs; |
localServiceIDs = NULL; |
resolvedServiceIDs = NULL; |
err = MoreSCCopyServiceIDs(NULL, &localServiceIDs, &resolvedServiceIDs); |
if (err == noErr) { |
serviceCount = CFArrayGetCount(localServiceIDs); |
for (serviceIndex = 0; serviceIndex < serviceCount; serviceIndex++) { |
CFStringRef userVisible; |
Boolean active; |
userVisible = NULL; |
err = MoreSCCopyUserVisibleNameOfService(NULL, (CFStringRef) CFArrayGetValueAtIndex(localServiceIDs, serviceIndex), &userVisible); |
if (err == noErr) { |
err = MoreSCIsServiceActive(NULL, (CFStringRef) CFArrayGetValueAtIndex(localServiceIDs, serviceIndex), &active); |
} |
if (err == noErr && !gRunQuiet) { |
fprintf(stderr, "#%ld %c ", serviceIndex, (active) ? ' ' : 'X'); |
CFShow(userVisible); |
CFShow(CFArrayGetValueAtIndex(localServiceIDs, serviceIndex)); |
CFShow(CFArrayGetValueAtIndex(resolvedServiceIDs, serviceIndex)); |
} |
CFQRelease(userVisible); |
if (err != noErr) { |
break; |
} |
} |
} |
CFQRelease(localServiceIDs); |
CFQRelease(resolvedServiceIDs); |
PrintTestResult(err, NULL); |
} |
static void TestDuplicateAndDeleteService(void) |
// A test of the service duplication and deleting routines. |
{ |
OSStatus err; |
CFArrayRef localServiceIDs; |
CFStringRef newServiceID; |
newServiceID = NULL; |
localServiceIDs = NULL; |
// Use NULL for the set ID to indicate that we're operating on |
// the current set. |
// |
// Can't use NULL for a service ID, so we have to come up with |
// a valid service ID first. We do this by choosing the first |
// service ID. |
err = MoreSCCopyServiceIDs(NULL, &localServiceIDs, NULL); |
if (err == noErr) { |
assert( CFArrayGetCount(localServiceIDs) > 0 ); |
err = MoreSCDuplicateService(NULL, (CFStringRef) CFArrayGetValueAtIndex(localServiceIDs, 0), CFSTR("Frog"), &newServiceID); |
} |
if (err == noErr) { |
if (!gRunQuiet) { |
fprintf(stderr, "New service ID is "); |
CFShow(newServiceID); |
} |
err = MoreSCDeleteService(NULL, newServiceID); |
} |
CFQRelease(localServiceIDs); |
CFQRelease(newServiceID); |
PrintTestResult(err, NULL); |
} |
static void TestRenameService(void) |
// A test of the MoreSCRenameService routine. |
{ |
OSStatus err; |
CFArrayRef localServiceIDs; |
CFStringRef originalName; |
CFStringRef newName; |
CFStringRef serviceZero; |
serviceZero = NULL; // quieten warning |
localServiceIDs = NULL; |
originalName = NULL; |
newName = NULL; |
// Use NULL for the set ID to indicate that we're operating on |
// the current set. |
// |
// Can't use NULL for a service ID, so we have to come up with |
// a valid service ID first. We do this by choosing the first |
// service ID. |
err = MoreSCCopyServiceIDs(NULL, &localServiceIDs, NULL); |
if (err == noErr) { |
assert( CFArrayGetCount(localServiceIDs) > 0 ); |
serviceZero = (CFStringRef) CFArrayGetValueAtIndex(localServiceIDs, 0); |
err = MoreSCCopyUserVisibleNameOfService(NULL, serviceZero, &originalName); |
} |
if (err == noErr) { |
err = MoreSCRenameService(NULL, serviceZero, CFSTR("Frog")); |
} |
if (err == noErr) { |
err = MoreSCCopyUserVisibleNameOfService(NULL, serviceZero, &newName); |
} |
if (err == noErr) { |
if ( ! CFEqual(newName, CFSTR("Frog")) ) { |
fprintf(stderr, "*** newName isn't 'Frog'\n"); |
CFShow(newName); |
} |
err = MoreSCRenameService(NULL, serviceZero, originalName); |
} |
if (err == noErr) { |
CFQRelease(newName); |
newName = NULL; |
err = MoreSCCopyUserVisibleNameOfService(NULL, serviceZero, &newName); |
} |
if (err == noErr) { |
if ( ! CFEqual(newName, originalName) ) { |
fprintf(stderr, "*** newName isn't the same as originalName\n"); |
CFShow(newName); |
} |
} |
CFQRelease(localServiceIDs); |
CFQRelease(originalName); |
CFQRelease(newName); |
PrintTestResult(err, NULL); |
} |
static void TestEnumerateEntities(void) |
// A test of the MoreSCCopyEntities routine. |
{ |
OSStatus err; |
CFArrayRef localServiceIDs; |
ItemCount entityCount; |
ItemCount entityIndex; |
CFArrayRef protocols; |
CFArrayRef values; |
// Use NULL for the set ID to indicate that we're operating on |
// the current set. |
// |
// Can't use NULL for a service ID, so we have to come up with |
// a valid service ID first. We do this by choosing the first |
// service ID. |
localServiceIDs = NULL; |
protocols = NULL; |
values = NULL; |
err = MoreSCCopyServiceIDs(NULL, &localServiceIDs, NULL); |
if (err == noErr) { |
assert(CFArrayGetCount(localServiceIDs) > 0); |
err = MoreSCCopyEntities(NULL, (CFStringRef) CFArrayGetValueAtIndex(localServiceIDs, 0), &protocols, &values); |
} |
if (err == noErr && !gRunQuiet) { |
entityCount = CFArrayGetCount(protocols); |
for (entityIndex = 0; entityIndex < entityCount; entityIndex++) { |
fprintf(stderr, "#%ld ", entityIndex); |
CFShow(CFArrayGetValueAtIndex(protocols, entityIndex)); |
CFShow(CFArrayGetValueAtIndex(values, entityIndex)); |
} |
} |
CFQRelease(localServiceIDs); |
CFQRelease(protocols); |
CFQRelease(values); |
PrintTestResult(err, NULL); |
} |
static OSStatus WorkaroundNetworkPrefsBug(CFDictionaryRef *entitiesDictPtr) |
// If this is an Ethernet interface and LCPEchoEnabled is false, |
// set it to true. This works around what I think is a bug in |
// the Network preferences panel <rdar://problem/3182846> where the |
// LCPEchoEnabled flag is mysteriously set to false for PCI Ethernet |
// interfaces. |
{ |
OSStatus err; |
CFStringRef hardwarePath[2]; |
CFStringRef lcpPath[2]; |
CFStringRef hardwareStr; |
CFNumberRef lcpValue; |
long enabled; |
hardwarePath[0] = kSCEntNetInterface; |
hardwarePath[1] = kSCPropNetInterfaceHardware; |
lcpPath[0] = kSCEntNetPPP; |
lcpPath[1] = kSCPropNetPPPLCPEchoEnabled; |
hardwareStr = NULL; // just to make debugging easier |
lcpValue = NULL; |
err = noErr; |
if ( CFQDictionaryGetValueAtPath(*entitiesDictPtr, (const void **) hardwarePath, 2, (const void **) &hardwareStr) == noErr |
&& CFEqual(hardwareStr, kSCEntNetEthernet) |
&& CFQDictionaryGetValueAtPath(*entitiesDictPtr, (const void **) lcpPath, 2, (const void **) &lcpValue) == noErr |
&& CFNumberGetValue(lcpValue, kCFNumberLongType, &enabled) |
&& (enabled == 0) ) { |
CFMutableDictionaryRef newDict; |
CFNumberRef numRef; |
if ( ! gRunQuiet ) { |
fprintf(stderr, "Applied workaround\n"); |
} |
numRef = NULL; |
newDict = CFDictionaryCreateMutableCopy(NULL, 0, *entitiesDictPtr); |
err = CFQError(newDict); |
if (err == noErr) { |
enabled = true; |
numRef = CFNumberCreate(NULL, kCFNumberLongType, &enabled); |
err = CFQError(numRef); |
} |
if (err == noErr) { |
err = CFQDictionarySetValueAtPath(newDict, (const void **) lcpPath, 2, numRef); |
} |
if (err == noErr) { |
CFQRelease(*entitiesDictPtr); |
*entitiesDictPtr = newDict; |
newDict = NULL; |
} |
CFQRelease(newDict); |
CFQRelease(numRef); |
} |
return err; |
} |
static OSStatus DeletePrivatePrefs(CFDictionaryRef *entitiesDictPtr) |
{ |
OSStatus err; |
CFStringRef privPath[2]; |
CFStringRef junkStr; |
privPath[0] = kSCEntNetProxies; |
privPath[1] = CFSTR("AppleProxyConfigurationSelected"); |
err = noErr; |
if ( CFQDictionaryGetValueAtPath(*entitiesDictPtr, (const void **) privPath, 2, (const void **) &junkStr) == noErr ) { |
CFMutableDictionaryRef newDict; |
newDict = CFDictionaryCreateMutableCopy(NULL, 0, *entitiesDictPtr); |
err = CFQError(newDict); |
if (err == noErr) { |
err = CFQDictionaryRemoveValueAtPath(newDict, (const void **) privPath, 2); |
} |
if (err == noErr) { |
CFQRelease(*entitiesDictPtr); |
*entitiesDictPtr = newDict; |
newDict = NULL; |
} |
CFQRelease(newDict); |
} |
return err; |
} |
static OSStatus CompareServices(CFStringRef newSet, CFStringRef newService, |
CFStringRef defSet, CFStringRef defService) |
// A service comparison routine used by the CompareSets routine. |
// This is godawful code but it's only test code and I don't have time to |
// make it more elegant at this point. |
{ |
OSStatus err; |
CFStringRef newUserVisible; |
CFStringRef defUserVisible; |
Boolean newActive; |
Boolean defActive; |
CFStringRef newBSD; |
CFStringRef defBSD; |
CFArrayRef newProtocols; |
CFArrayRef defProtocols; |
CFArrayRef newValues; |
CFArrayRef defValues; |
CFDictionaryRef newDict; |
CFDictionaryRef defDict; |
newUserVisible = NULL; |
defUserVisible = NULL; |
newBSD = NULL; |
defBSD = NULL; |
newProtocols = NULL; |
defProtocols = NULL; |
newValues = NULL; |
defValues = NULL; |
newDict = NULL; |
defDict = NULL; |
#if 0 |
fprintf(stderr, "newSet, defSet, newService, defService = \n"); |
CFShow(newSet); |
CFShow(defSet); |
CFShow(newService); |
CFShow(defService); |
#endif |
// User visible name |
err = MoreSCCopyUserVisibleNameOfService(newSet, newService, &newUserVisible); |
if (err == noErr) { |
err = MoreSCCopyUserVisibleNameOfService(defSet, defService, &defUserVisible); |
} |
if (err == noErr) { |
if ( ! CFEqual(newUserVisible, defUserVisible) ) { |
fprintf(stderr, "*** User visible names don't match\n"); |
CFShow(newUserVisible); |
CFShow(defUserVisible); |
err = -1; |
} |
} |
// Active flag |
if (err == noErr) { |
err = MoreSCIsServiceActive(newSet, newService, &newActive); |
} |
if (err == noErr) { |
err = MoreSCIsServiceActive(defSet, defService, &defActive); |
} |
if (err == noErr) { |
if ( newActive != defActive ) { |
fprintf(stderr, "*** Active flags don't match\n"); |
err = -1; |
} |
} |
// BSD name |
if (err == noErr) { |
err = MoreSCCopyBSDNameOfService(newSet, newService, &newBSD); |
} |
if (err == noErr) { |
err = MoreSCCopyBSDNameOfService(defSet, defService, &defBSD); |
} |
if (err == noErr) { |
if ( ! CFEqual(newBSD, defBSD) ) { |
fprintf(stderr, "*** BSD names don't match\n"); |
CFShow(newBSD); |
CFShow(defBSD); |
err = -1; |
} |
} |
// Entities in service |
if (err == noErr) { |
err = MoreSCCopyEntities(newSet, newService, &newProtocols, &newValues); |
} |
if (err == noErr) { |
err = MoreSCCopyEntities(defSet, defService, &defProtocols, &defValues); |
} |
if (err == noErr) { |
err = CFQDictionaryCreateWithArrayOfKeysAndValues(newProtocols, newValues, &newDict); |
} |
if (err == noErr) { |
err = CFQDictionaryCreateWithArrayOfKeysAndValues(defProtocols, defValues, &defDict); |
} |
if (err == noErr) { |
err = WorkaroundNetworkPrefsBug(&defDict); |
} |
if (err == noErr) { |
err = DeletePrivatePrefs(&defDict); |
} |
if (err == noErr) { |
if ( ! CFEqual(newDict, defDict) ) { |
fprintf(stderr, "*** Entities don't match\n"); |
PrintPropertyList(newDict); |
PrintPropertyList(defDict); |
err = -1; |
} |
} |
CFQRelease(newUserVisible); |
CFQRelease(defUserVisible); |
CFQRelease(newBSD); |
CFQRelease(defBSD); |
CFQRelease(newProtocols); |
CFQRelease(defProtocols); |
CFQRelease(newValues); |
CFQRelease(defValues); |
CFQRelease(newDict); |
CFQRelease(defDict); |
return err; |
} |
static OSStatus CompareGlobalEntities(CFStringRef newSet, CFStringRef defSet) |
// A routine to compare global entities, taking care to ignore |
// certain entities that aren't relevant. This is used by the |
// CompareSets routine. |
// |
// This is godawful code but it's only test code and I don't have time to |
// make it more elegant at this point. |
{ |
OSStatus err; |
CFArrayRef newProtocols; |
CFArrayRef defProtocols; |
CFArrayRef newValues; |
CFArrayRef defValues; |
CFDictionaryRef newDict; |
CFDictionaryRef defDict; |
CFMutableDictionaryRef newMutableDict; |
CFMutableDictionaryRef defMutableDict; |
CFStringRef serviceOrderPath[2]; |
newProtocols = NULL; |
defProtocols = NULL; |
newValues = NULL; |
defValues = NULL; |
newDict = NULL; |
defDict = NULL; |
newMutableDict = NULL; |
defMutableDict = NULL; |
// Create two mutable dictionaries, ones for the new entities and the |
// other for the def entities. |
err = MoreSCCopyEntities(newSet, NULL, &newProtocols, &newValues); |
if (err == noErr) { |
err = MoreSCCopyEntities(defSet, NULL, &defProtocols, &defValues); |
} |
if (err == noErr) { |
err = CFQDictionaryCreateWithArrayOfKeysAndValues(newProtocols, newValues, &newDict); |
} |
if (err == noErr) { |
err = CFQDictionaryCreateWithArrayOfKeysAndValues(defProtocols, defValues, &defDict); |
} |
if (err == noErr) { |
newMutableDict = CFDictionaryCreateMutableCopy(NULL, 0, newDict); |
if (newMutableDict == NULL) { |
err = coreFoundationUnknownErr; |
} |
} |
if (err == noErr) { |
defMutableDict = CFDictionaryCreateMutableCopy(NULL, 0, defDict); |
if (defMutableDict == NULL) { |
err = coreFoundationUnknownErr; |
} |
} |
// Remove the elementsl at "IPv4/ServiceOrder" because they will never |
// compare equal because they contain different service IDs. |
serviceOrderPath[0] = kSCEntNetIPv4; |
serviceOrderPath[1] = kSCPropNetServiceOrder; |
if (err == noErr) { |
err = CFQDictionaryRemoveValueAtPath(newMutableDict, (const void **) serviceOrderPath, 2); |
} |
if (err == noErr) { |
err = CFQDictionaryRemoveValueAtPath(defMutableDict, (const void **) serviceOrderPath, 2); |
} |
// Now compare the dictionaries. |
if (err == noErr) { |
if ( ! CFEqual(newMutableDict, defMutableDict) ) { |
fprintf(stderr, "*** Global entities don't match\n"); |
PrintPropertyList(newMutableDict); |
PrintPropertyList(defMutableDict); |
err = -1; |
} |
} |
CFQRelease(newProtocols); |
CFQRelease(defProtocols); |
CFQRelease(newValues); |
CFQRelease(defValues); |
CFQRelease(newDict); |
CFQRelease(defDict); |
CFQRelease(newMutableDict); |
CFQRelease(defMutableDict); |
return err; |
} |
static OSStatus CompareSets(CFStringRef newSet, CFStringRef defSet) |
// A routine to compare two sets to see if they're identical. |
// TestCreateSet uses this to check that the newly created set |
// was created correctly. |
{ |
OSStatus err; |
CFArrayRef newServices; |
CFArrayRef defServices; |
CFIndex serviceCount; |
CFIndex serviceIndex; |
// MoreSCCopyUserVisibleNameOfSet not called because |
// user visible name of sets will be different. |
newServices = NULL; |
defServices = NULL; |
// Services within the set |
err = MoreSCCopyServiceIDs(newSet, &newServices, NULL); |
if (err == noErr) { |
err = MoreSCCopyServiceIDs(defSet, &defServices, NULL); |
} |
if (err == noErr) { |
if ( CFArrayGetCount(newServices) != CFArrayGetCount(defServices) ) { |
fprintf(stderr, "*** Service counts not equal.\n"); |
fprintf(stderr, "%ld\n", CFArrayGetCount(newServices)); |
fprintf(stderr, "%ld\n", CFArrayGetCount(defServices)); |
err = -1; |
} |
} |
if (err == noErr) { |
serviceCount = CFArrayGetCount(newServices); |
for (serviceIndex = 0; serviceIndex < serviceCount; serviceIndex++) { |
err = CompareServices(newSet, (CFStringRef) CFArrayGetValueAtIndex(newServices, serviceIndex), |
defSet, (CFStringRef) CFArrayGetValueAtIndex(defServices, serviceIndex)); |
if (err != noErr) { |
break; |
} |
} |
} |
// Global entities |
if (err == noErr) { |
err = CompareGlobalEntities(newSet, defSet); |
} |
CFQRelease(newServices); |
CFQRelease(defServices); |
return err; |
} |
extern Boolean gMoreSCFCCLScannerBugForBugCompatibility; |
static void TestCreateSet(void) |
// A routine to test the set creation and deletion code. |
// It compares the newly created set against a set called |
// "DefaultLocationForMoreSCFTest" to ensure that the set |
// created by MoreSCF matches a set created by the control |
// panel. |
{ |
OSStatus err; |
OSStatus err2; |
CFStringRef newSetID; |
CFStringRef templateSetID; |
newSetID = NULL; |
templateSetID = NULL; |
err = MoreSCFindSetByUserVisibleNameAndCopyID(CFSTR(kDefaultLocationName), &templateSetID); |
if (err != noErr || templateSetID == NULL) { |
fprintf(stderr, "*** TestCreateSet requires that you create a default set named '%s' using the Network preferences panel.\n", kDefaultLocationName); |
err = -1; |
} |
gMoreSCFCCLScannerBugForBugCompatibility = true; |
if (err == noErr) { |
err = MoreSCNewSet(CFSTR("Frog"), &newSetID); |
} |
gMoreSCFCCLScannerBugForBugCompatibility = false; |
if (err == noErr) { |
if (!gRunQuiet) { |
fprintf(stderr, "New set ID is "); |
CFShow(newSetID); |
} |
err = CompareSets(newSetID, templateSetID); |
err2 = MoreSCDeleteSet(newSetID); |
if (err == noErr) { |
err = err2; |
} |
} |
CFQRelease(newSetID); |
CFQRelease(templateSetID); |
PrintTestResult(err, NULL); |
} |
static void TestISP(void) |
// A routine to test the ISP support routines. |
{ |
OSStatus err; |
CFStringRef setID; |
CFStringRef setID2; |
CFStringRef newSetID; |
setID = NULL; |
setID2 = NULL; |
newSetID = NULL; |
err = MoreSCFindSetByUserVisibleNameAndCopyID(CFSTR(kDefaultLocationName), &setID); |
if (err == noErr) { |
if (setID == NULL) { |
fprintf(stderr, "*** Couldn't find the set '%s'\n", kDefaultLocationName); |
} else if (!gRunQuiet) { |
fprintf(stderr, "Set ID for '%s' is\n", kDefaultLocationName); |
CFShow(setID); |
} |
} |
if (err == noErr) { |
err = MoreSCFindSetByUserVisibleNameAndCopyID(CFSTR("Who would give a set such a silly name"), &setID2); |
if (setID2 != NULL) { |
fprintf(stderr, "*** Found set that shouldn't exist\n"); |
} |
} |
if (err == noErr) { |
MoreSCPPPDigest ppp; |
BlockZero(&ppp, sizeof(ppp)); |
ppp.active = true; |
ppp.authName = CFSTR("Quinn"); |
ppp.authPassword = CFSTR("eskimo"); |
ppp.commRemoteAddress = CFSTR("123 4567"); |
err = MoreSCMakeNewDialupSet(NULL, CFSTR("Frog PPP"), NULL, &ppp, NULL, NULL, &newSetID); |
} |
if (err == noErr) { |
err = MoreSCDeleteSet(newSetID); |
} |
CFQRelease(newSetID); |
newSetID = NULL; |
PrintTestResult(err, "dialup"); |
err = noErr; |
if (err == noErr) { |
MoreSCPPPDigest ppp; |
BlockZero(&ppp, sizeof(ppp)); |
ppp.active = true; |
ppp.authName = CFSTR("Quinn"); |
ppp.authPassword = CFSTR("eskimo"); |
err = MoreSCMakeNewPPPoESet(NULL, CFSTR("Frog PPPoE"), &ppp, NULL, NULL, &newSetID); |
} |
if (err == noErr) { |
err = MoreSCDeleteSet(newSetID); |
} |
CFQRelease(setID); |
CFQRelease(setID2); |
CFQRelease(newSetID); |
PrintTestResult(err, "PPPoE"); |
} |
static void TestAppleTalk(void) |
// A routine to test the AppleTalk on/off routines. |
{ |
OSStatus err; |
Boolean originalState; |
Boolean newState; |
err = MoreSCIsAppleTalkActive(NULL, &originalState); |
if (err == noErr && !gRunQuiet) { |
fprintf(stderr, "AppleTalk is %s.\n", originalState ? "active" : "inactive"); |
} |
if (err == noErr) { |
err = MoreSCSetAppleTalkActive(NULL, !originalState); |
} |
if (err == noErr) { |
err = MoreSCIsAppleTalkActive(NULL, &newState); |
} |
if (err == noErr) { |
if (!gRunQuiet) { |
fprintf(stderr, "AppleTalk is now %s.\n", newState ? "active" : "inactive"); |
} |
if (newState != !originalState) { |
fprintf(stderr, "*** Failed to change state.\n"); |
} |
} |
if (err == noErr) { |
err = MoreSCSetAppleTalkActive(NULL, originalState); |
} |
if (err == noErr) { |
err = MoreSCIsAppleTalkActive(NULL, &newState); |
} |
if (err == noErr) { |
if (!gRunQuiet) { |
fprintf(stderr, "AppleTalk is now %s.\n", newState ? "active" : "inactive"); |
} |
if (newState != originalState) { |
fprintf(stderr, "*** Failed to restore state.\n"); |
} |
} |
PrintTestResult(err, NULL); |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ***** Ad Hoc Debugging |
// This code is for debugging individual modules and hasn't been integrated |
// into a formal test yet. |
static void DebugModemScriptSearch(void) |
// Used for debugging the modem script code. |
{ |
OSStatus err; |
CFArrayRef cclArray; |
CFIndex indexOfDefaultCCL; |
cclArray = NULL; |
err = MoreSCCreateCCLArray(&cclArray, &indexOfDefaultCCL); |
if (err == noErr) { |
CFIndex i; |
CFIndex c; |
c = CFArrayGetCount(cclArray); |
fprintf(stderr, "CCL Count = %ld\n", c); |
for (i = 0; i < c; i++) { |
fprintf(stderr, "%3ld %c ", i, i == indexOfDefaultCCL ? '*' : ' '); |
CFShow(CFArrayGetValueAtIndex(cclArray, i)); |
} |
} |
CFQRelease(cclArray); |
if (err == noErr) { |
fprintf(stderr, "Success!\n"); |
} else { |
fprintf(stderr, "*** Failed with error %ld!\n", err); |
} |
} |
static void DebugCreateProxiesEntity(void) |
// Used for debugging MoreSCCreateProxiesEntity. |
{ |
OSStatus err; |
MoreSCProxiesDigest proxy; |
CFDictionaryRef entity; |
BlockZero(&proxy, sizeof(proxy)); |
entity = NULL; |
err = MoreSCCreateProxiesEntity(&proxy, &entity); |
if (err == noErr) { |
PrintPropertyList(entity); |
} |
CFQRelease(entity); |
} |
static void DebugOpenClose(void) |
// Used for debugging MoreSCOpen/Close. |
{ |
OSStatus err; |
int_t oldUID; |
// Simple open/close. |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 1) failed with error %ld!\n", err); |
} |
// Open and close dirty. |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, true); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 2) failed with error %ld!\n", err); |
} |
// Open locked and close not dirty. |
err = MoreSCOpen(true, true); |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 3) failed with error %ld!\n", err); |
} |
// Open locked and close dirty. |
err = MoreSCOpen(true, true); |
MoreSCClose(&err, true); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 4) failed with error %ld!\n", err); |
} |
// Simple recursive open with no privileges needed. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, false); |
} |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 5) failed with error %ld!\n", err); |
} |
// Recursive open with no lock but dirty made. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, false); |
} |
MoreSCClose(&err, true); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 6) failed with error %ld!\n", err); |
} |
// Recursive open with lock and no dirty. |
err = MoreSCOpen(true, true); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, false); |
} |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 7) failed with error %ld!\n", err); |
} |
// Recursive open with lock and dirty. |
err = MoreSCOpen(true, true); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, false); |
} |
MoreSCClose(&err, true); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 8) failed with error %ld!\n", err); |
} |
// Recursive open with inner no lock and dirty. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
MoreSCClose(&err, true); |
} |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 9) failed with error %ld!\n", err); |
} |
// Recursive open with inner lock and no dirty. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = MoreSCOpen(true, true); |
MoreSCClose(&err, false); |
} |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 10) failed with error %ld!\n", err); |
} |
// Recursive open with inner lock and dirty. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = MoreSCOpen(true, true); |
MoreSCClose(&err, true); |
} |
MoreSCClose(&err, false); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 11) failed with error %ld!\n", err); |
} |
// Recursive open with both inner and outer lock and dirty. |
err = MoreSCOpen(true, true); |
if (err == noErr) { |
err = MoreSCOpen(true, true); |
MoreSCClose(&err, true); |
} |
MoreSCClose(&err, true); |
if (err != noErr) { |
fprintf(stderr, "*** DebugOpenClose (step 12) failed with error %ld!\n", err); |
} |
// Open/close with error. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = -1; |
} |
MoreSCClose(&err, false); |
if (err != -1) { |
fprintf(stderr, "*** DebugOpenClose (step 13) failed with error %ld!\n", err); |
} |
// Open/close with dirty and error. |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
err = -1; |
} |
MoreSCClose(&err, true); |
if (err != -1) { |
fprintf(stderr, "*** DebugOpenClose (step 14) failed with error %ld!\n", err); |
} |
// Open/close with lock, dirty and error. |
err = MoreSCOpen(true, true); |
if (err == noErr) { |
err = -1; |
} |
MoreSCClose(&err, true); |
if (err != -1) { |
fprintf(stderr, "*** DebugOpenClose (step 15) failed with error %ld!\n", err); |
} |
// Switch to the nobody user. |
oldUID = seteuid(-2); |
// Lock but no dirty, should trigger error on the open. |
err = MoreSCOpen(true, true); |
assert(err == kSCStatusAccessError); |
MoreSCClose(&err, false); |
if (err != kSCStatusAccessError) { |
fprintf(stderr, "*** DebugOpenClose (step 16) failed with error %ld!\n", err); |
} |
// No lock but dirty, should trigger error on the close. |
err = MoreSCOpen(false, false); |
assert(err == noErr); |
MoreSCClose(&err, true); |
if (err != kSCStatusAccessError) { |
fprintf(stderr, "*** DebugOpenClose (step 16) failed with error %ld!\n", err); |
} |
(void) seteuid(oldUID); |
} |
static void DebugCreateSet(void) |
{ |
OSStatus err; |
err = MoreSCNewSet(CFSTR("Frog"), NULL); |
if (err == noErr) { |
fprintf(stderr, "Success!\n"); |
} else { |
fprintf(stderr, "*** Failed with error %ld!\n", err); |
} |
} |
#define kPPPoESetName CFSTR("MoreSCF DebugCreatePPPoE Set Name") |
static void DebugCreatePPPoE(void) |
{ |
OSStatus err; |
CFStringRef setID; |
setID = NULL; |
err = MoreSCFindSetByUserVisibleNameAndCopyID(kPPPoESetName, &setID); |
if (err == noErr && setID != NULL) { |
err = MoreSCDeleteSet(setID); |
CFQRelease(setID); |
setID = NULL; |
} |
if (err == noErr) { |
MoreSCPPPDigest ppp; |
BlockZero(&ppp, sizeof(ppp)); |
ppp.active = true; |
ppp.authName = CFSTR("Quinn"); |
ppp.authPassword = CFSTR("eskimo"); |
err = MoreSCMakeNewPPPoESet(NULL, kPPPoESetName, &ppp, NULL, NULL, &setID); |
} |
if (err == noErr) { |
fprintf(stderr, "PPPoE set ID is "); |
CFShow(setID); |
} |
CFQRelease(setID); |
if (err == noErr) { |
fprintf(stderr, "Success!\n"); |
} else { |
fprintf(stderr, "*** Failed with error %ld\n", err); |
} |
} |
static void DebugTrivialCreate(void) |
{ |
OSStatus err; |
CFStringRef setID; |
setID = NULL; |
err = MoreSCSetClient(CFSTR("MyClient")); |
if (err == noErr) { |
err = MoreSCOpen(false, false); |
} |
if (err == noErr) { |
err = MoreSCNewSet(CFSTR("My New Set"), &setID); |
MoreSCClose(&err, true); |
} |
CFQRelease(setID); |
PrintTestResult(err, NULL); |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ***** Test Engine |
static void PrintPropertyListCallback(CFTypeRef key, CFTypeRef node, void *context) |
// A callback routine used by PrintPropertyList to print |
// a property list in a nicely formatted way. |
{ |
#pragma unused(key) |
int i; |
int depth; |
depth = (int)context; |
for (i = 0; i < depth; i++) { |
fprintf(stderr, " "); |
} |
{ |
CFStringRef fullDesc; |
CFStringRef typeDesc; |
CFStringRef valueDesc; |
fullDesc = NULL; |
typeDesc = CFCopyTypeIDDescription(CFGetTypeID(node)); |
valueDesc = NULL; |
if ( CFQPropertyListIsLeaf(node) ) { |
if ( CFGetTypeID(node) == CFStringGetTypeID() ) { |
valueDesc = (CFStringRef) CFRetain(node); |
} else if ( CFGetTypeID(node) == CFNumberGetTypeID() ) { |
valueDesc = (CFStringRef) CFRetain(node); |
} else { |
valueDesc = CFCopyDescription(node); |
} |
fullDesc = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ : %@ [%d] = %@"), key, typeDesc, CFGetRetainCount(node), valueDesc); |
} else { |
fullDesc = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ : %@ [%d]"), key, typeDesc, CFGetRetainCount(node)); |
} |
CFShow(fullDesc); |
CFQRelease(fullDesc); |
CFQRelease(valueDesc); |
CFQRelease(typeDesc); |
} |
if ( ! CFQPropertyListIsLeaf(node) ) { |
CFQPropertyListShallowApplyFunction(node, PrintPropertyListCallback, (void *) (depth + 1) ); |
} |
} |
extern void PrintPropertyList(CFPropertyListRef propList) |
// This routine prints a CFPropertyList in a nicely formatted way. |
{ |
PrintPropertyListCallback(CFSTR("ROOT"), propList, (void *) 0); |
} |
static void RefCounter(CFTypeRef node, void *context) |
// A callback routine that adds node's reference count |
// to a global total. Used by TotalAllRefCounts. |
{ |
(*((SInt32 *)context)) += CFGetRetainCount(node); |
} |
static SInt32 TotalAllRefCounts(SCPreferencesRef prefsRef, CFArrayRef allKeys) |
// Given a connection to the SCF preferences database and an array |
// of preference keys, this routine calculates the total of all |
// of the reference counts of all of the nodes in all of the SCF |
// preferences. I use this routine to check for reference count leaks |
// in my use of the SCF preferences database. |
{ |
CFIndex keyCount; |
CFIndex keyIndex; |
SInt32 result; |
result = 0; |
keyCount = CFArrayGetCount(allKeys); |
for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { |
CFPropertyListRef thisPref; |
thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast |
CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &result ); |
} |
return result; |
} |
#if 0 |
static void PrintAllPreferences(SCPreferencesRef prefsRef, CFArrayRef allKeys) |
// This routine prints all of the preferences in the SCF preferences |
// database. I use it during debugging. |
{ |
CFIndex keyCount; |
CFIndex keyIndex; |
keyCount = CFArrayGetCount(allKeys); |
for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { |
CFPropertyListRef thisPref; |
thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast |
PrintPropertyList(thisPref); |
} |
} |
#endif |
typedef void (*TestFunc)(void); |
static void LeakTest(TestFunc tester) |
// Given a test name and a pointer to a test function, |
// this routine calls the test function repeatedly to check |
// for SCF preferences reference count leaks. |
{ |
OSStatus err; |
SCPreferencesRef prefsRef; |
CFArrayRef allKeys; |
int i; |
SInt32 startCount; |
SInt32 endCount; |
prefsRef = NULL; |
// This is kinda cheesy. We need to use the same SCPreferencesRef |
// that MoreSCF uses because otherwise we can't see the reference |
// count changes done by MoreSCF. Ideally, we wouldn't want to run |
// within a MoreSCOpen/MoreSCClose pair because then our changes |
// aren't necessarily being committed to the database. However, |
// given the current MoreSCF architecture, where the SCPreferenceRef |
// is only valid inside the MoreSCOpen/MoreSCClose pair, that's |
// exactly what we have to do. |
allKeys = NULL; |
err = MoreSCOpen(false, false); |
if (err == noErr) { |
prefsRef = MoreSCGetSCPreferencesRef(); |
} |
// Now get a copy of the array of all keys in the database. |
// We make this copy using CFPropertyListCreateDeepCopy so we |
// know that it's completely independent of the SCF. |
if (err == noErr) { |
CFArrayRef allKeysOrig; |
allKeysOrig = NULL; |
allKeysOrig = SCPreferencesCopyKeyList(prefsRef); |
if (allKeysOrig == NULL) { |
err = SCError(); |
} |
if (err == noErr) { |
allKeys = (CFArrayRef) CFPropertyListCreateDeepCopy(NULL, allKeysOrig, kCFPropertyListMutableContainersAndLeaves); // C++ requires cast |
if (allKeys == NULL) { |
err = coreFoundationUnknownErr; |
} |
} |
CFQRelease(allKeysOrig); |
} |
// Now do the reference counting test. Call the tester function |
// a few times to allow the refcounts to stabilise. Then get |
// a summ of all the nodes in all of the keys in SCF. Then |
// run the test a 10 more times and get another count. If |
// the counts are different, we're in trouble. |
if (err == noErr) { |
for (i = 0; i < 3; i++) { |
tester(); |
} |
startCount = TotalAllRefCounts(prefsRef, allKeys); |
for (i = 0; i < 10; i++) { |
tester(); |
} |
endCount = TotalAllRefCounts(prefsRef, allKeys); |
if (startCount != endCount) { |
CFIndex keyCount; |
CFIndex keyIndex; |
fprintf(stderr, "*** Leaked a total of %ld reference counts.\n", endCount - startCount); |
keyCount = CFArrayGetCount(allKeys); |
for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { |
CFPropertyListRef thisPref; |
// The code conditionalised by gCheckLeaksDebug is helpful when |
// you're actively trying to track down a leak. |
startCount = 0; |
thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast |
CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &startCount ); |
if (gCheckLeaksDebug) { |
fprintf(stderr, "*** BEFORE %ld ***\n", keyIndex); |
PrintPropertyList(thisPref); |
} |
tester(); |
endCount = 0; |
thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast |
CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &endCount ); |
if (gCheckLeaksDebug) { |
fprintf(stderr, "*** AFTER %ld ***\n", keyIndex); |
PrintPropertyList(thisPref); |
} |
if (startCount != endCount) { |
fprintf(stderr, "*** %ld leaked reference counts at key index %ld\n", endCount - startCount, keyIndex); |
CFShow(CFArrayGetValueAtIndex(allKeys, keyIndex)); |
} |
} |
} |
} |
MoreSCClose(&err, false); |
CFQRelease(allKeys); |
if (err != noErr) { |
fprintf(stderr, "*** Failed with error %ld!\n", err); |
} |
} |
static void PrintTestResult(OSStatus errNum, const char *subTestName) |
{ |
if (errNum == noErr) { |
if ( ! gRunQuiet ) { |
if (subTestName == NULL) { |
fprintf(stderr, "Success!\n"); |
} else { |
fprintf(stderr, "Success! (%s)\n", subTestName); |
} |
} |
} else { |
if (subTestName == NULL) { |
fprintf(stderr, "*** Failed with error %ld\n", errNum); |
} else { |
fprintf(stderr, "*** Failed (%s) with error %ld\n", subTestName, errNum); |
} |
} |
} |
struct TestInfo { |
const char * testName; |
TestFunc tester; |
Boolean needsRoot; |
Boolean debugOnly; |
}; |
typedef struct TestInfo TestInfo; |
static void RunTest(const TestInfo *theTest) |
{ |
Boolean runTheTest; |
runTheTest = true; |
if ( gRunQuiet && gCheckLeaks ) { |
fprintf(stderr, "LeakTest(%s)\n", theTest->testName); |
} else if ( ! gRunQuiet ) { |
fprintf(stderr, "%s\n", theTest->testName); |
} |
if ( theTest->needsRoot ) { |
if (gNoRootTests) { |
fprintf(stderr, "*** Did not run test because it requires root.\n"); |
runTheTest = false; |
} else if ( geteuid() != 0 ) { |
fprintf(stderr, "*** Did not run because the test requires root.\n"); |
runTheTest = false; |
} |
} |
if (runTheTest) { |
if ( gCheckLeaks ) { |
LeakTest(theTest->tester); |
} else { |
theTest->tester(); |
} |
} |
if ( ! gRunQuiet ) { |
fprintf(stderr, "\n"); |
} |
} |
static const TestInfo kTests[] = { |
{ "TestPortScanner", TestPortScanner, false, false }, |
{ "TestPortScannerOld", TestPortScannerOld, false, false }, |
{ "TestCCLScanner", TestCCLScanner, false, false }, |
{ "TestCopySetsDict", TestCopySetsDict, false, false }, |
{ "TestSetEnumerationAndSwitch", TestSetEnumerationAndSwitch, true, false }, |
{ "TestDuplicateAndDeleteSet", TestDuplicateAndDeleteSet, true, false }, |
{ "TestRenameSet", TestRenameSet, true, false }, |
{ "TestServiceEnumerate", TestServiceEnumerate, false, false }, |
{ "TestDuplicateAndDeleteService", TestDuplicateAndDeleteService, true, false }, |
{ "TestRenameService", TestRenameService, true, false }, |
{ "TestEnumerateEntities", TestEnumerateEntities, false, false }, |
{ "TestCreateSet", TestCreateSet, true, false }, |
{ "TestISP", TestISP, true, false }, |
{ "TestAppleTalk", TestAppleTalk, true, false }, |
{ "DebugModemScriptSearch", DebugModemScriptSearch, false, true }, |
{ "DebugCreateProxiesEntity", DebugCreateProxiesEntity, false, true }, |
{ "DebugOpenClose", DebugOpenClose, true, true }, |
{ "DebugCreateSet", DebugCreateSet, true, true }, |
{ "DebugCreatePPPoE", DebugCreatePPPoE, true, true }, |
{ "DebugTrivialCreate", DebugTrivialCreate, true, true }, |
{ NULL, NULL } |
}; |
static void RunTestsMatching(const char *testName) |
{ |
int testIndex; |
testIndex = 0; |
while ( kTests[testIndex].testName != NULL ) { |
// Run the test if we're running all tests and the test isn't a debug-only |
// test, or if the user has explicitly asked for it. |
if ( ((testName == NULL) && ! kTests[testIndex].debugOnly) || ( (testName != NULL) && (strcmp(testName, kTests[testIndex].testName) == 0)) ) { |
RunTest(&kTests[testIndex]); |
} |
testIndex += 1; |
} |
} |
static void PrintUsage(void) |
{ |
size_t testIndex; |
static const char * kPrefixen[2] = { |
"Tests names:", |
"Debug test names: (not run as part of automated test suite)" |
}; |
Boolean debugOnly; |
fprintf(stderr, "Usage: %s [-u] [-q] [-l] [-L] [ TestName... ] \n", gProgramName); |
fprintf(stderr, " -u only run root tests if running as root\n"); |
fprintf(stderr, " -q quiet mode\n"); |
fprintf(stderr, " -l run tests repeatedly to check for leaks (implies -q)\n"); |
fprintf(stderr, " -L leak checks with debugging output (implies -q)\n"); |
fprintf(stderr, "\n"); |
for (debugOnly = 0; debugOnly < 2; debugOnly++) { |
fprintf(stderr, " %s\n", kPrefixen[debugOnly]); |
testIndex = 0; |
while ( kTests[testIndex].testName != NULL ) { |
if ( kTests[testIndex].debugOnly == debugOnly ) { |
fprintf( |
stderr, |
" %s%s\n", |
kTests[testIndex].testName, |
kTests[testIndex].needsRoot ? " *" : "" |
); |
} |
testIndex += 1; |
} |
} |
fprintf(stderr, " * must be run as root\n"); |
} |
int main(int argc, char *argv[]) |
{ |
int ch; |
int argIndex; |
// Set up gProgramName to be the last path element of argv[0]. |
gProgramName = strrchr(argv[0], '/'); |
if (gProgramName == NULL) { |
gProgramName = argv[0]; |
} else { |
gProgramName += 1; |
} |
fprintf(stderr, "%s\n", gProgramName); |
// Parse arguments. |
do { |
ch = getopt(argc, argv, "uqLl"); |
if (ch != -1) { |
switch (ch) { |
case 'u': |
gNoRootTests = true; |
break; |
case 'q': |
gRunQuiet = true; |
break; |
case 'L': |
gCheckLeaksDebug = true; |
// fall through |
case 'l': |
gCheckLeaks = true; |
gRunQuiet = true; |
break; |
case '?': |
default: |
PrintUsage(); |
exit(1); |
break; |
} |
} |
} while (ch != -1); |
if (optind == argc) { |
fprintf(stderr, "Running all tests\n"); |
fprintf(stderr, "\n"); |
RunTestsMatching(NULL); |
} else { |
for (argIndex = optind; argIndex < argc; argIndex++) { |
RunTestsMatching(argv[argIndex]); |
} |
} |
return 0; |
} |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-06-07