When trying to compile the following code for iphone in xcode
void removeGrid(int x,int y) {
//for(id *item in self) {
//if(item.position == ccp(x*32, y*32)) {
// printf("good");
//}
//printf("%#",item);
// }
char rrs[8];
sprintf(rrs,"01%d%d",x/32,y/32);
int aTag = [[[NSString alloc] initWithBytes:rrs length:sizeof(rrs) encoding:NSASCIIStringEncoding] intValue];
//NSAssert( aTag != kCCNodeTagInvalid, #"Invalid tag");
CCNode *child = [self getChildByTag:aTag]; //here it is simply getting a single chil
if (child == nil)
CCLOG(#"cocos2d: removeChildByTag: child not found!");
else
[self removeChild:child cleanup:true];
}
The compiler says "self was not declared in this scope". I'm new to objc and cocos2d, but this seems to be the way most tutorials access objects in the scene. Am I missing something?
Solved. This turned out to be one of Xcode's quirks. Since the function declaration was in c++, it was not able to access objective c self functions for some reason. Changing the declaration to an objective c allowed it to access all the functions. Doesn't make much sense to me, but it now works fine.
For those of you wondering, yes, the file had a .mm extension.
Related
I'd like to implement XCTest unit-tests, but don't really want to use XCode to do it. It's not obvious how to do this, and I'm wondering if this is even possible?
From what I've found so far, one could get the impression that XCTest is completely dependent on XCode. I've found the xcodebuild test command-line support, but that depends on finding an XCode project or workspace.
Have I any options here, or do I just rip out the existing SenTestingKit code and revert to some home-brew unit test code? I have some such code to hand, but it's not the Right Thing To Do.
Rationale/history:
This is not just me being old-skool. I have an Objective-C program which I last touched two years ago, for which I had developed a reasonable set of unit tests based on SenTestingKit. Now I come back to this code – I may at least have to rebuild the thing, because of intervening library changes – I discover that SenTestingKit has disappeared, to be replaced by XCTest. Oh well....
This code was not developed using XCode, so there isn't a .project file associated with it, and the tests were up to now happily managed using SenTestingKit's main programs, and a Makefile check target (that's partly being old-skool, again, partly a lack of fondness for IDEs, and partly this having been an experiment with Objective-C, so originally sticking with what I know).
Thanks, #stanislaw-pankevich, for a great answer. Here, for completeness, I'm including (more-or-less) the complete test program which I ended up with, which includes a couple of extra details and comments.
(This is a complete program from my point of view, since it tests functions
defined in util.h, which isn't included here)
File UtilTest.h:
#import <XCTest/XCTest.h>
#interface UtilTest : XCTestCase
#end
File UtilTest.m:
#import "UtilTest.h"
#import "../util.h" // the definition of the functions being tested
#implementation UtilTest
// We could add methods setUp and tearDown here.
// Every no-arg method which starts test... is included as a test-case.
- (void)testPathCanonicalization
{
XCTAssertEqualObjects(canonicalisePath("/p1/./p2///p3/..//f3"), #"/p1/p2/f3");
}
#end
Driver program runtests.m (this is the main program, which the makefile actually invokes to run all the tests):
#import "UtilTest.h"
#import <XCTest/XCTestObservationCenter.h>
// Define my Observation object -- I only have to do this in one place
#interface BrownieTestObservation : NSObject<XCTestObservation>
#property (assign, nonatomic) NSUInteger testsFailed;
#property (assign, nonatomic) NSUInteger testsCalled;
#end
#implementation BrownieTestObservation
- (instancetype)init {
self = [super init];
self.testsFailed = 0;
return self;
}
// We can add various other functions here, to be informed about
// various events: see XCTestObservation at
// https://developer.apple.com/reference/xctest?language=objc
- (void)testSuiteWillStart:(XCTestSuite *)testSuite {
NSLog(#"suite %#...", [testSuite name]);
self.testsCalled = 0;
}
- (void)testSuiteDidFinish:(XCTestSuite *)testSuite {
NSLog(#"...suite %# (%tu tests)", [testSuite name], self.testsCalled);
}
- (void)testCaseWillStart:(XCTestSuite *)testCase {
NSLog(#" test case: %#", [testCase name]);
self.testsCalled++;
}
- (void)testCase:(XCTestCase *)testCase didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber {
NSLog(#" FAILED: %#, %# (%#:%tu)", testCase, description, filePath, lineNumber);
self.testsFailed++;
}
#end
int main(int argc, char** argv) {
XCTestObservationCenter *center = [XCTestObservationCenter sharedTestObservationCenter];
BrownieTestObservation *observer = [BrownieTestObservation new];
[center addTestObserver:observer];
Class classes[] = { [UtilTest class], }; // add other classes here
int nclasses = sizeof(classes)/sizeof(classes[0]);
for (int i=0; i<nclasses; i++) {
XCTestSuite *suite = [XCTestSuite testSuiteForTestCaseClass:classes[i]];
[suite runTest];
}
int rval = 0;
if (observer.testsFailed > 0) {
NSLog(#"runtests: %tu failures", observer.testsFailed);
rval = 1;
}
return rval;
}
Makefile:
FRAMEWORKS=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks
TESTCASES=UtilTest
%.o: %.m
clang -F$(FRAMEWORKS) -c $<
check: runtests
./runtests 2>runtests.stderr
runtests: runtests.o $(TESTCASES:=.o) ../libmylib.a
cc -o $# $< -framework Cocoa -F$(FRAMEWORKS) -rpath $(FRAMEWORKS) \
-framework XCTest $(TESTCASES:=.o) -L.. -lmylib
Notes:
The XCTestObserver class is now deprecated, and replaced by XCTestObservation.
The results of tests are sent to a shared XCTestObservationCenter, which unfortunately chatters distractingly to stderr (which therefore has to be redirected elsewhere) – it doesn't seem possible to avoid that and have them sent only to my observation centre instead. In my actual program, I replaced the NSLog calls in runtests.m with a function which chatters to stdout, which I could therefore distinguish from the chatter going to the default ObservationCenter.
See also the overview documentation
(presumes that you're using XCode),
...the XCTest API documentation,
...and the notes in the headers of the files at (eg) /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers
If you are looking for Xcode-based solutions see this and its linked solutions for examples.
For complete non-Xcode-based solution continue reading.
I used to ask similar answer a few years ago: Is there any non-Xcode-based command line unit testing tool for Objective-C? but things changed since then.
One interesting feature that appeared in XCTest over time is ability to run your custom test suites. I used to implement them successfully for my research needs, here is an example code which is a command line Mac OS application:
#interface FooTest : XCTestCase
#end
#implementation FooTest
- (void)testFoo {
XCTAssert(YES);
}
- (void)testFoo2 {
XCTAssert(NO);
}
#end
#interface TestObserver : NSObject <XCTestObservation>
#property (assign, nonatomic) NSUInteger testsFailed;
#end
#implementation TestObserver
- (instancetype)init {
self = [super init];
self.testsFailed = 0;
return self;
}
- (void)testBundleWillStart:(NSBundle *)testBundle {
NSLog(#"testBundleWillStart: %#", testBundle);
}
- (void)testBundleDidFinish:(NSBundle *)testBundle {
NSLog(#"testBundleDidFinish: %#", testBundle);
}
- (void)testSuiteWillStart:(XCTestSuite *)testSuite {
NSLog(#"testSuiteWillStart: %#", testSuite);
}
- (void)testCaseWillStart:(XCTestCase *)testCase {
NSLog(#"testCaseWillStart: %#", testCase);
}
- (void)testSuiteDidFinish:(XCTestSuite *)testSuite {
NSLog(#"testSuiteDidFinish: %#", testSuite);
}
- (void)testSuite:(XCTestSuite *)testSuite didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber {
NSLog(#"testSuite:didFailWithDescription:inFile:atLine: %# %# %# %tu",
testSuite, description, filePath, lineNumber);
}
- (void)testCase:(XCTestCase *)testCase didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber {
NSLog(#"testCase:didFailWithDescription:inFile:atLine: %# %# %# %tu",
testCase, description, filePath, lineNumber);
self.testsFailed++;
}
- (void)testCaseDidFinish:(XCTestCase *)testCase {
NSLog(#"testCaseWillFinish: %#", testCase);
}
#end
int RunXCTests() {
XCTestObserver *testObserver = [XCTestObserver new];
XCTestObservationCenter *center = [XCTestObservationCenter sharedTestObservationCenter];
[center addTestObserver:testObserver];
XCTestSuite *suite = [XCTestSuite defaultTestSuite];
[suite runTest];
NSLog(#"RunXCTests: tests failed: %tu", testObserver.testsFailed);
if (testObserver.testsFailed > 0) {
return 1;
}
return 0;
}
To compile this kind of code you will need to show a path to the folder where XCTest is located something like:
# in your Makefile
clang -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks XCTestDriver.m
Don't expect the code to compile but it should give you an idea. Feel free to ask if you have any questions. Also follow the headers of XCTest framework to learn more about its classes and their docs.
This one works ok:
Build your .xctest target as usual. By default it will be added to Plugins of the host app, but the location is irrelevant.
Create a runner command line tool with the code below.
Update: Xcode ships runner that is fairly standalone in /Applications/Xcode.app/Contents/Developer/usr/bin/xctest
You may use this one, if you don't want to create your own simple runner.
Run the tool with the full path to your test suite.
Sample runner code:
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
int main(int argc, const char* argv[]) {
#autoreleasepool {
XCTestSuite *suite = [XCTestSuite testSuiteForBundlePath:
[NSString stringWithUTF8String:argv[1]]];
[suite runTest];
// Note that XCTestSuite is very shy in terms of errors,
// so make sure that it loaded anything indeed:
if (!suite.testRun.testCaseCount) return 1;
return suite.testRun.hasSucceeded;
}
}
in an NSViewController subclass this BOOL returns "fault is (null)" in the console:
Submission *sub = [self representedObject];
BOOL fault = [sub isFault];
NSLog(#"fault is : %#", fault);
i do have the sub managedObject's properties, so i know that its available.
testing with committedValuesForKeys (right below the above in the same method) gives me the expected property values in the console.
NSLog(#"[sub committedValuesForKeys:nil] is : %#", [sub
committedValuesForKeys:nil]);
self here is an NSCollectionViewItem, a subclass of NSViewController.
There are some other cleaner ways to do this:
BOOL fault = YES;
NSLog(fault ? #"Yes" : #"No");
and
BOOL fault = YES;
NSLog(#"Bool fault: %d",fault);
via How to print Boolean flag in NSLog?
You can't check the BOOLs value like that. Instead do:
if (fault) {
NSLog(#"Fault is true");
} else {
NSLog(#"Fault is false");
}
I would like to know how to execute an applescript from a cocoa application passing parameters.
I have seen how easy is to execute applescripts with no parameters in other questions here at stackoverflow, however the use NSAppleScript class, in which, i haven't seen no method that solve my problem. Does anyone have any idea.
I would like a Cocoa code with the same effect o this shell:
osascript teste.applescript "snow:Users:MyUser:Desktop:MyFolder" "snow:Users:MyUser:Desktop:Example:"
So it may run this AppleScript.
on run argv
set source to (item 1 of argv)
set destiny to (item 2 of argv)
tell application "Finder" to make new alias file at destiny to source
0
end run
Any help is appreciated. Thanks in advance.
Look at my GitHub repository, I have a category of NSAppleEventDescriptor that makes it much easier to create NSAppleEventDescriptor to call different AppleScript procedures with arguments, and coercion to and from many AppleScript typed.
NSAppleEventDescriptor-NDCoercion
I found easier to follow this piece code. I took a code from here and modified it to my purpose.
- (BOOL) executeScriptWithPath:(NSString*)path function:(NSString*)functionName andArguments:(NSArray*)scriptArgumentArray
{
BOOL executionSucceed = NO;
NSAppleScript * appleScript;
NSAppleEventDescriptor * thisApplication, *containerEvent;
NSURL * pathURL = [NSURL fileURLWithPath:path];
NSDictionary * appleScriptCreationError = nil;
appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL error:&appleScriptCreationError];
if (appleScriptCreationError)
{
NSLog([NSString stringWithFormat:#"Could not instantiate applescript %#",appleScriptCreationError]);
}
else
{
if (functionName && [functionName length])
{
/* If we have a functionName (and potentially arguments), we build
* an NSAppleEvent to execute the script. */
//Get a descriptor for ourself
int pid = [[NSProcessInfo processInfo] processIdentifier];
thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID
bytes:&pid
length:sizeof(pid)];
//Create the container event
//We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework...
#define kASAppleScriptSuite 'ascr'
#define kASSubroutineEvent 'psbr'
#define keyASSubroutineName 'snam'
containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite
eventID:kASSubroutineEvent
targetDescriptor:thisApplication
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
//Set the target function
[containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:functionName]
forKeyword:keyASSubroutineName];
//Pass arguments - arguments is expecting an NSArray with only NSString objects
if ([scriptArgumentArray count])
{
NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor];
NSString *object;
for (object in scriptArgumentArray) {
[arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object]
atIndex:([arguments numberOfItems] + 1)]; //This +1 seems wrong... but it's not
}
[containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject];
[arguments release];
}
//Execute the event
NSDictionary * executionError = nil;
NSAppleEventDescriptor * result = [appleScript executeAppleEvent:containerEvent error:&executionError];
if (executionError != nil)
{
NSLog([NSString stringWithFormat:#"error while executing script. Error %#",executionError]);
}
else
{
NSLog(#"Script execution has succeed. Result(%#)",result);
executionSucceed = YES;
}
}
else
{
NSDictionary * executionError = nil;
NSAppleEventDescriptor * result = [appleScript executeAndReturnError:&executionError];
if (executionError != nil)
{
NSLog([NSString stringWithFormat:#"error while executing script. Error %#",executionError]);
}
else
{
NSLog(#"Script execution has succeed. Result(%#)",result);
executionSucceed = YES;
}
}
}
[appleScript release];
return executionSucceed;
}
Technical Note TN2084
Using AppleScript Scripts in Cocoa Applications
Even though your application is written in Objective-C using Cocoa, you can use AppleScript scripts to perform certain operations. This Technical Note explains how to integrate and execute AppleScripts from within your Cocoa application. It discusses how to leverage the NSAppleScript class and the use of NSAppleEventDescriptor to send data to the receiver.
https://developer.apple.com/library/archive/technotes/tn2084/_index.html
https://applescriptlibrary.files.wordpress.com/2013/11/technical-note-tn2084-using-applescript-scripts-in-cocoa-applications.pdf
Swift 4 version, modified from the code here:
https://gist.github.com/chbeer/3666e4b7b2e71eb47b15eaae63d4192f
import Carbon
static func runAppleScript(_ url: URL) {
var appleScriptError: NSDictionary? = nil
guard let script = NSAppleScript(contentsOf: url, error: &appleScriptError) else {
return
}
let message = NSAppleEventDescriptor(string: "String parameter")
let parameters = NSAppleEventDescriptor(listDescriptor: ())
parameters.insert(message, at: 1)
var psn = ProcessSerialNumber(highLongOfPSN: UInt32(0), lowLongOfPSN: UInt32(kCurrentProcess))
let target = NSAppleEventDescriptor(descriptorType: typeProcessSerialNumber, bytes: &psn, length: MemoryLayout<ProcessSerialNumber>.size)
let handler = NSAppleEventDescriptor(string: "MyMethodName")
let event = NSAppleEventDescriptor.appleEvent(withEventClass: AEEventClass(kASAppleScriptSuite), eventID: AEEventID(kASSubroutineEvent), targetDescriptor: target, returnID: AEReturnID(kAutoGenerateReturnID), transactionID: AETransactionID(kAnyTransactionID))
event.setParam(handler, forKeyword: AEKeyword(keyASSubroutineName))
event.setParam(parameters, forKeyword: AEKeyword(keyDirectObject))
var executeError: NSDictionary? = nil
script.executeAppleEvent(event, error: &executeError)
if let executeError = executeError {
print("ERROR: \(executeError)")
}
}
For running the apple script:
on MyMethodName(theParameter)
display dialog theParameter
end MyMethodName
I'm not all too familiar with AppleScript, but I seem to remember that they are heavily based on (the rather crappy) Apple Events mechanism which dates back to the days where the 56k Modem was the coolest Gadget in your house.
Therefore I'd guess that you're looking for executeAppleEvent:error: which is part of NSAppleScript. Maybe you can find some information on how to encapsulate execution arguments in the instance of NSAppleEventDescriptor that you have to pass along with this function.
On an iPhone I can use
[[UIDevice currentDevice] uniqueIdentifier];
to get a string which identifies this device. Is there anything equal in OSX ? I didn't find anything. I just want to identify the Mac which started the application. Can you help me ?
Apple has a technote on uniquely identifying a mac. Here's a loosely modified version of the code Apple has posted in that technote... don't forget to link your project against IOKit.framework in order to build this:
#import <IOKit/IOKitLib.h>
- (NSString *)serialNumber
{
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef serialNumberAsCFString = NULL;
if (platformExpert) {
serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
IOObjectRelease(platformExpert);
}
NSString *serialNumberAsNSString = nil;
if (serialNumberAsCFString) {
serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
CFRelease(serialNumberAsCFString);
}
return serialNumberAsNSString;
}
Swift 2 Answer
This answer augments Jarret Hardie's 2011 answer. It's a Swift 2 String extension. I've added inline comments to explain what I did and why, since navigating whether or not an object needs to be released can be tricky here.
extension String {
static func macSerialNumber() -> String {
// Get the platform expert
let platformExpert: io_service_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
// Get the serial number as a CFString ( actually as Unmanaged<AnyObject>! )
let serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey, kCFAllocatorDefault, 0);
// Release the platform expert (we're responsible)
IOObjectRelease(platformExpert);
// Take the unretained value of the unmanaged-any-object
// (so we're not responsible for releasing it)
// and pass it back as a String or, if it fails, an empty string
return (serialNumberAsCFString.takeUnretainedValue() as? String) ?? ""
}
}
Alternatively, the function could return String? and the last line could not return an empty string. That might make it easier to recognize the extreme situations where the serial number could not be retrieved (such as the repaired-Mac-motherboard scenario harrisg mentioned in his comment to Jerret's answer).
I also verified proper memory management with Instruments.
I hope someone finds it useful!
Thanks. Works perfectly after changing
serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
TO
serialNumberAsNSString = [NSString stringWithString:(__bridge NSString *)serialNumberAsCFString];
the __bridge is recommended by Xcode itself.
I have got a problem when unit testing a class. When running my test, it compiles without any errors but then it crashes (it does NOT fail in the sense of an assertion not being met), displaying the following error message:
/Developer/Tools/RunPlatformUnitTests.include:451:0 Test rig '/Developer/Platforms
/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/Developer/usr/bin/otest'
exited abnormally with code 134 (it may have crashed).
Here's my code:
The class' interface:
#interface AbstractActionModel : NSObject
{
NSString* mName;
ActionType mType; // enum
float mDuration;
float mRepeatCount;
float mDelay;
NSArray* mTriggerAreas;
}
The implementation:
- (void) dealloc
{
[mTriggerAreas release];
[super dealloc];
}
- (id) initWithConfigData: (NSDictionary*) theConfigData
{
NSAssert(nil != theConfigData, #"theConfigData cannot be nil");
self = [super init];
if (self)
{
self.name = [theConfigData objectForKey:ACTION_NAME];
self.type = [[theConfigData objectForKey:ACTION_TYPE] intValue];
self.duration = [[theConfigData objectForKey:ACTION_DURATION] floatValue];
self.delay = [[theConfigData objectForKey:ACTION_DELAY] floatValue];
self.repeatCount = [[theConfigData objectForKey:ACTION_REPEAT_COUNT] floatValue];
self.triggerAreas = [theConfigData objectForKey:ACTION_TRIGGER_AREAS];
}
return self;
}
Here's the test code:
- (void) testCreateAction
{
SoundActionModel* testSoundAction = (SoundActionModel*)[SoundActionModelFactory createActionModel:self.actionConfig];
STAssertNotNil(testSoundAction, #"returned object must not be nil");
}
The Factory's createActionModel: method:
+ (AbstractActionModel*) createActionModel:(NSDictionary *)config
{
NSAssert(config != nil, #"config must not be nil");
SoundActionModel* retVal = [[[SoundActionModel alloc] initWithConfigData:config] autorelease];
return retVal;
}
As previously mentioned: The code compiles, and it runs when testCreateAction is commented out. The problem does not seem to be the test itself (i.e. its assertion).
Telling from these postings (similar problem 1, similar problem 2) it seems to be a bug in XCode, but these links point to problems which arise when using Core Data (which I don't) or OCMock (which I don't, either - at least not knowingly).
Can someone tell me how to solve this kind of problem? If it turns out to be a bug, a workaround would be very much appreciated.
I also had this problem when starting out with OCUnit. This is caused by attempting to execute as test that is setup in Logic test mode, rather than application test mode. If the code under test has some dependency on Cocoa or Cocoa Touch, this code must be run with a target set up for application test.
The fact that the test runner itself crashes looks like an xcode bug to me as AppCode will continue passed this point.
A good source for setting up these tests is here