Commits

Ronald Oussoren  committed cb10ddb

Add some tests for plugin bundles

This adds a simple plugin host and a testcase that uses
this to perform a very basic test of plugin initialization.

  • Participants
  • Parent commits 11be5f1

Comments (0)

Files changed (5)

File py2app_tests/basic_plugin/main.py

+import sys
+import Foundation
+
+class BasicPlugin (Foundation.NSObject):
+    def performCommand_(self, cmd):
+        print "+ %s"%(cmd,)
+        sys.stdout.flush()

File py2app_tests/basic_plugin/setup.py

+from setuptools import setup
+
+plist = dict(
+    NSPrincipleClass="BasicPlugin"
+)
+
+setup(
+    name='BasicPlugin',
+    plugin=['main.py'],
+    options=dict(py2app=dict(
+        extension=".bundle",
+        plist=plist,
+    )),
+)

File py2app_tests/bundle_loader.m

+#import <Foundation/Foundation.h>
+#include <stdio.h>
+
+static NSMutableDictionary* pluginMap;
+
+@interface PluginObject : NSObject
+{}
+-(void)performCommand:(NSString*)command;
+@end
+
+
+static int loadBundle(NSString* bundlePath)
+{
+	if (pluginMap == nil) {
+		pluginMap = [NSMutableDictionary dictionary];
+		if (pluginMap == nil) {
+			printf("** Cannot allocate plugin map\n");
+			return -1;
+		}
+		[pluginMap retain];
+	}
+
+	NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
+	if (bundle == NULL) {
+		printf("** Cannot load bundle %s\n", [bundlePath UTF8String]);
+		return -1;
+	}
+
+	Class pluginClass = [bundle principalClass];
+	if (pluginClass == Nil) {
+		printf("** No principle class in %s\n", [bundlePath UTF8String]);
+		return -1;
+	}
+
+	PluginObject* pluginObject = [[pluginClass alloc] init];
+	if (pluginObject == Nil) {
+		printf("** No principle class in %s\n", [bundlePath UTF8String]);
+		return -1;
+	}
+
+	[pluginMap setObject:pluginObject forKey:[bundlePath lastPathComponent]];
+	return 0;
+}
+
+
+static int 
+perform_commands(void)
+{
+static char gBuf[1024];
+	char* ln;
+	while ((ln = fgets(gBuf, 1024, stdin)) != NULL) {
+		char* e = strchr(ln, '\n');
+		if (e) { *e  = '\0'; }
+		char* cmd = strchr(ln, ':');
+		if (cmd == NULL) {
+			if (strcmp(ln, "quit") == 0) {
+				return (0);
+			}
+			printf("* UNKNOWN COMMAND: %s\n", ln);
+		} else {
+			*cmd++ = '\0';
+			NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+			PluginObject* pluginObject = [pluginMap objectForKey:[NSString stringWithUTF8String:ln]];
+			if (pluginObject == NULL) {
+				printf("* NO OBJECT: %s\n", cmd);
+				continue;
+			}
+			[pluginObject performCommand: [NSString stringWithUTF8String:cmd]];
+			[pool release];
+		}
+	}
+	return 0;
+}
+
+
+int main(int argc, char** argv)
+{
+	int i, r;
+	NSAutoreleasePool* pool;
+
+	pool = [[NSAutoreleasePool alloc] init];
+	for (i = 1; i < argc; i++) {
+		r = loadBundle([NSString stringWithUTF8String:argv[i]]);
+		if (r != 0) {
+			return 2;
+		}
+	}
+	printf("+ loaded %d bundles\n", argc - 1);
+	fflush(stdout);
+	[pool release];
+	return perform_commands();
+}
+

File py2app_tests/test_basic_app.py

 
 
 class TestBasicApp (unittest.TestCase):
+    py2app_args = []
 
     # Basic setup code
     #
     def setUpClass(cls):
         p = subprocess.Popen([
                 sys.executable,
-                    'setup.py', 'py2app'],
+                    'setup.py', 'py2app'] + cls.py2app_args,
             cwd = os.path.join(DIR_NAME, 'basic_app'),
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
         p.stdin.close()
         p.stdout.close()
 
+# XXX: too look into
+#class TestBasicAliasApp (TestBasicApp):
+#    py2app_args = [ '--alias', ]
+
 if __name__ == "__main__":
     unittest.main()
 

File py2app_tests/test_basic_plugin.py

+"""
+Simular to test_basic_app, but for plugin bundles
+"""
+import sys
+if (sys.version_info[0] == 2 and sys.version_info[:2] >= (2,7)) or \
+        (sys.version_info[0] == 3 and sys.version_info[:2] >= (3,2)):
+    import unittest
+else:
+    import unittest2 as unittest
+
+import subprocess
+import shutil
+import time
+import os
+import signal
+from distutils.sysconfig import get_config_var
+
+DIR_NAME=os.path.dirname(os.path.abspath(__file__))
+
+
+class TestBasicPlugin (unittest.TestCase):
+    py2app_args = []
+
+    # Basic setup code
+    #
+    # The code in this block needs to be moved to
+    # a base-class.
+    @classmethod
+    def setUpClass(cls):
+        cmd = [ sys.executable, 'setup.py', 'py2app'] + cls.py2app_args
+        
+        p = subprocess.Popen(
+            cmd,
+            cwd = os.path.join(DIR_NAME, 'basic_plugin'),
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+            close_fds=True)
+        lines = p.communicate()[0]
+        if p.wait() != 0:
+            print (lines)
+            raise AssertionError("Creating basic_plugin bundle failed")
+
+        p = subprocess.Popen([
+            'cc'] +  get_config_var('LDFLAGS').split() + [ 
+                '-o', 'bundle_loader', os.path.join(DIR_NAME, 'bundle_loader.m'), 
+                '-framework', 'Foundation'],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+            close_fds=True)
+        lines = p.communicate()[0]
+        if p.wait() != 0:
+            print (lines)
+            raise AssertionError("Creating bundle_loader failed")
+
+    @classmethod
+    def tearDownClass(cls):
+        if os.path.exists(os.path.join(DIR_NAME, 'basic_plugin/build')):
+            shutil.rmtree(os.path.join(DIR_NAME, 'basic_plugin/build'))
+
+        if os.path.exists(os.path.join(DIR_NAME, 'basic_plugin/dist')):
+            shutil.rmtree(os.path.join(DIR_NAME, 'basic_plugin/dist'))
+
+        if os.path.exists('bundle_loader'):
+            os.unlink('bundle_loader')
+
+    def start_app(self):
+        # Start the test app, return a subprocess object where
+        # stdin and stdout are connected to pipes.
+        cmd = ['./bundle_loader',
+                    os.path.join(DIR_NAME,
+                                'basic_plugin/dist/BasicPlugin.bundle'),
+        ]
+        p = subprocess.Popen(cmd,
+                stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE,
+                close_fds=True,
+                )
+                #stderr=subprocess.STDOUT)
+        return p
+
+    def wait_with_timeout(self, proc, timeout=10):
+        for i in range(timeout):
+            x = proc.poll()
+            if x is None:
+                time.sleep(1)
+            else:
+                return x
+
+        os.kill(proc.pid, signal.SIGKILL)
+        return proc.wait()
+
+    #
+    # End of setup code
+    # 
+
+    def test_basic_start(self):
+        p = self.start_app()
+        p.stdout.readline()
+
+        p.stdin.write('BasicPlugin.bundle:test startup\n'.encode('latin1'))
+        p.stdin.flush()
+
+        v = p.stdout.readline()
+        self.assertEquals(v.strip(), '+ test startup')
+
+        p.stdin.close()
+        p.stdout.close()
+
+        exit = self.wait_with_timeout(p)
+        self.assertEqual(exit, 0)
+
+class TestBasicAliasPlugin (TestBasicPlugin):
+    py2app_args = [ '--alias' ]
+
+if __name__ == "__main__":
+    unittest.main()
+