Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH 1/1] test: Make map script a command line client
@ 2013-01-11 16:44 Christian Fetzer
  2013-01-11 20:48 ` Marcel Holtmann
  0 siblings, 1 reply; 4+ messages in thread
From: Christian Fetzer @ 2013-01-11 16:44 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Christian Fetzer

From: Christian Fetzer <christian.fetzer@bmw-carit.de>

Rework the map-client test script into an interactive command line client.
Now multiple MCE functions can be called in one active session.
The script also allows to specify all filters or optional parameters including
auto completion.

Change-Id: I9c9ede2bc958009c757384177cf06c081d984c98
---
 test/map-client | 320 ++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 218 insertions(+), 102 deletions(-)

diff --git a/test/map-client b/test/map-client
index 9fb7a5e..c5c899a 100755
--- a/test/map-client
+++ b/test/map-client
@@ -4,10 +4,13 @@ from __future__ import absolute_import, print_function, unicode_literals
 
 import gobject
 
+import cmd
+import shlex
 import sys
 import os
 import dbus
 import dbus.mainloop.glib
+import threading
 from optparse import OptionParser
 
 from pprint import pformat
@@ -31,49 +34,39 @@ def unwrap(x):
         return tuple(map(unwrap, x))
 
     if isinstance(x, dict):
-        return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()])
+        return dict([(unwrap(k), unwrap(v)) for k, v in x.items()])
 
-    for t in [unicode, str, long, int, float, bool]:
+    if sys.version_info >= (3, 0):
+        coversion_types = [str, int, float, bool]
+    else:
+        coversion_types = [unicode, str, long, int, float, bool]
+
+    for t in coversion_types:
         if isinstance(x, t):
             return t(x)
 
     return x
 
 def parse_options():
+	parser.add_option("-s", "--source", dest="source",
+			help="Source / local address to use", metavar="SOURCE")
 	parser.add_option("-d", "--device", dest="device",
 			help="Device to connect", metavar="DEVICE")
-	parser.add_option("-c", "--chdir", dest="new_dir",
-			help="Change current directory to DIR", metavar="DIR")
-	parser.add_option("-l", "--lsdir", action="store_true", dest="ls_dir",
-			help="List folders in current directory")
-	parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
-	parser.add_option("-L", "--lsmsg", action="store", dest="ls_msg",
-			help="List messages in supplied CWD subdir")
-	parser.add_option("-g", "--get", action="store", dest="get_msg",
-			help="Get message contents")
-	parser.add_option("--get-properties", action="store", dest="get_msg_properties",
-			help="Get message properties")
-	parser.add_option("--mark-read", action="store", dest="mark_msg_read",
-			help="Marks the messages as read")
-	parser.add_option("--mark-unread", action="store", dest="mark_msg_unread",
-			help="Marks the messages as unread")
-	parser.add_option("--mark-deleted", action="store", dest="mark_msg_deleted",
-			help="Deletes the message from the folder")
-	parser.add_option("--mark-undeleted", action="store", dest="mark_msg_undeleted",
-			help="Undeletes the message")
-	parser.add_option("-u", "--update-inbox", action="store_true", dest="update_inbox",
-			help="Checks for new mails")
+	parser.add_option("-p", "--port", dest="port", default=0,
+			help="RFCOMM port to connect", metavar="PORT")
+	parser.add_option("-v", "--verbose", action="store_true",
+			dest="verbose")
 
 	return parser.parse_args()
 
-def set_folder(session, new_dir):
-	session.SetFolder(new_dir)
-
-class MapClient:
+class MapClient(cmd.Cmd):
 	def __init__(self, session_path, verbose=False):
+		cmd.Cmd.__init__(self)
+		cmd.Cmd.prompt = "MCE> "
 		self.progress = 0
 		self.transfer_path = None
 		self.props = dict()
+		self.dir = dict()
 		self.verbose = verbose
 		self.path = session_path
 		bus = dbus.SessionBus()
@@ -85,8 +78,15 @@ class MapClient:
 			signal_name="PropertiesChanged",
 			path_keyword="path")
 
-	def create_transfer_reply(self, reply):
-		(path, properties) = reply
+	def create_transfer_reply_get(self, path, properties):
+		self.dir[path] = "in";
+		self.create_transfer_reply(path, properties)
+
+	def create_transfer_reply_put(self, path, properties):
+		self.dir[path] = "out";
+		self.create_transfer_reply(path, properties)
+
+	def create_transfer_reply(self, path, properties):
 		self.transfer_path = path
 		self.props[path] = properties
 		if self.verbose:
@@ -98,22 +98,25 @@ class MapClient:
 			print("Operation succeeded")
 
 	def error(self, err):
-		print err
-		mainloop.quit()
+		print(err)
 
 	def transfer_complete(self, path):
 		if self.verbose:
 			print("Transfer finished")
 		properties = self.props.get(path)
+		print(path)
+		print(self.dir)
 		if properties == None:
 			return
-		f = open(properties["Filename"], "r")
-		os.remove(properties["Filename"])
-		print(f.readlines())
+		if self.dir.get(path) == "in":
+			f = open(properties["Filename"], "r")
+			os.remove(properties["Filename"])
+			print(f.readlines())
 
-	def transfer_error(self, path):
-		print("Transfer %s error" % path)
-		mainloop.quit()
+	def transfer_error(self, code, message, path):
+		if path != self.transfer_path:
+			return
+		print("Transfer finished with error %s: %s" % (code, message))
 
 	def properties_changed(self, interface, properties, invalidated, path):
 		req = self.props.get(path)
@@ -128,46 +131,171 @@ class MapClient:
 			self.transfer_error(path)
 			return
 
-	def set_folder(self, new_dir):
-		self.map.SetFolder(new_dir)
-
-	def list_folders(self):
-		for i in self.map.ListFolders(dict()):
-			print("%s/" % (i["Name"]))
-
-	def list_messages(self, folder):
-		ret = self.map.ListMessages(folder, dict())
-		print(pformat(unwrap(ret)))
-
-	def get_message(self, handle):
-		self.map.ListMessages("", dict())
+	def emptyline(self):
+		pass
+
+	def do_EOF(self, args):
+		""" Quit """
+		return True
+
+	def do_exit(self, args):
+		""" Quit """
+		return True
+
+	# SetFolder
+	def do_SetFolder(self, new_dir):
+		""" Set working directory for current session """
+		try:
+			self.map.SetFolder(new_dir)
+		except dbus.exceptions.DBusException as e:
+			print("Failed:", e)
+
+	def do_cd(self, new_dir):
+		self.do_SetFolder(new_dir)
+
+	# ListFolders
+	list_folder_parms = {
+				'MaxCount': lambda x: dbus.UInt16(x),
+				'Offset': lambda x: dbus.UInt16(x)
+				}
+
+	def do_ListFolders(self, args):
+		""" List directories in current working directory """
+		parms={}
+		if args:
+			try:
+				for i in args.split(' '):
+					k,v = i.split('=')
+					parms[k] = self.list_folder_parms[k](v)
+			except:
+				print("Syntax error")
+				return
+
+		try:
+			for i in self.map.ListFolders(parms):
+				print("%s/" % (i["Name"]))
+		except dbus.exceptions.DBusException as e:
+			print("Failed:", e)
+
+	def complete_ListFolders(self, text, args, begidx, endidx):
+		if not text:
+			completions = list(self.list_folder_parms.keys())
+		else:
+			completions = [ f + "=" for f in
+						self.list_folder_parms.keys()
+							if f.startswith(text) ]
+		return completions
+
+	# ListFilterFields
+	def do_ListFilterFields(self, args):
+		""" List all available fields that can be used as filters """
+		for i in self.map.ListFilterFields():
+			print(i)
+
+	# ListMessages
+	list_msg_parms = { 'Folder': lambda x: x,
+				'MaxCount': lambda x: dbus.UInt16(x),
+				'Offset': lambda x: dbus.UInt16(x),
+				'SubjectLength': lambda x: dbus.Byte(int(x)),
+				'Fields': lambda x: x.split(','),
+				'Types': lambda x: x.split(','),
+				'PeriodBegin': lambda x: dbus.String(x),
+				'PeriodEnd': lambda x: dbus.String(x),
+				'Read': lambda x:
+					dbus.Boolean(x.lower() in
+							("yes", "true", "1")),
+				'Recipient': lambda x: dbus.String(x),
+				'Sender': lambda x: dbus.String(x),
+				'Priority': lambda x:
+					dbus.Boolean(x.lower() in
+							("yes", "true", "1"))
+				}
+
+	def do_ListMessages(self, args):
+		""" List messages in current working directory """
+		parms={}
+		if args:
+			try:
+				for i in shlex.split(args):
+					k,v = i.split('=')
+					parms[k] = self.list_msg_parms[k](v)
+			except Exception as e:
+				print("Syntax error", e)
+				return
+
+		try:
+			ret = self.map.ListMessages(parms.pop('Folder', ''),
+									parms)
+			print(pformat(unwrap(ret)))
+		except dbus.exceptions.DBusException as e:
+			print("Failed:", e)
+
+	def complete_ListMessages(self, text, args, begidx, endidx):
+		if not text:
+			completions = list(self.list_msg_parms.keys())
+		else:
+			completions = [ f + "=" for f in
+						self.list_msg_parms.keys()
+							if f.startswith(text) ]
+		return completions
+
+	def do_ls(self, args):
+		print("Folders:")
+		self.do_ListFolders(args)
+		print("Messages:")
+		self.do_ListMessages(args)
+
+	# GetMessage
+	def do_GetMessage(self, handle):
+		""" Download message """
 		path = self.path + "/message" + handle
 		obj = bus.get_object(BUS_NAME, path)
 		msg = dbus.Interface(obj, MESSAGE_INTERFACE)
-		msg.Get("", True, reply_handler=self.create_transfer_reply,
+		msg.Get("", True, reply_handler=self.create_transfer_reply_get,
 						error_handler=self.error)
 
-	def get_message_properties(self, handle):
-		self.map.ListMessages("", dict())
-		path = self.path + "/message" + handle
-		obj = bus.get_object(BUS_NAME, path)
-		msg = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
-		ret = msg.GetAll(MESSAGE_INTERFACE)
-		print(pformat(unwrap(ret)))
-
-	def set_message_property(self, handle, prop, flag):
-		self.map.ListMessages("", dict())
-		path = self.path + "/message" + handle
-		obj = bus.get_object(BUS_NAME, path)
-		msg = dbus.Interface(obj, MESSAGE_INTERFACE)
-		msg.SetProperty (prop, flag);
-
-	def update_inbox(self):
-		self.map.UpdateInbox()
-
+	# GetMessageProperties
+	def do_GetMessageProperties(self, handle):
+		""" Returns all properties for the message """
+		try:
+			path = self.path + "/message" + handle
+			obj = bus.get_object(BUS_NAME, path)
+			msg = dbus.Interface(obj,
+					"org.freedesktop.DBus.Properties")
+			ret = msg.GetAll(MESSAGE_INTERFACE)
+			print(pformat(unwrap(ret)))
+		except Exception as e:
+			print("Error", e)
+			return
 
-if  __name__ == '__main__':
+	# SetMessageProperties
+	def do_SetMessageProperty(self, args):
+		"""Sets value to the mentioned property (Read, Delete) """
+		try:
+			handle, parm_line = args.split(' ')
+			parm, value = parm_line.split('=')
+		except Exception as e:
+			print("Syntax error", e)
+			return
 
+		try:
+			path = self.path + "/message" + handle
+			obj = bus.get_object(BUS_NAME, path)
+			msg = dbus.Interface(obj, MESSAGE_INTERFACE)
+			msg.SetProperty(parm, value in ("yes", "true", "1"));
+		except dbus.exceptions.DBusException as e:
+			print("Failed:", e)
+
+	# UpdateInbox
+	def do_UpdateInbox(self, line):
+		""" Request remote to update its inbox """
+		try:
+			self.map.UpdateInbox()
+		except dbus.exceptions.DBusException as e:
+			print("Failed:", e)
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.threads_init()
 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
 	parser = OptionParser()
@@ -179,44 +307,32 @@ if  __name__ == '__main__':
 		exit(0)
 
 	bus = dbus.SessionBus()
+	gobject.threads_init()
 	mainloop = gobject.MainLoop()
 
-	client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
-							CLIENT_INTERFACE)
-
-	print("Creating Session")
-	path = client.CreateSession(options.device, { "Target": "map" })
+	mainloop_thread = threading.Thread(target=mainloop.run)
+	mainloop_thread.start()
 
-	map_client = MapClient(path, options.verbose)
-
-	if options.new_dir:
-		map_client.set_folder(options.new_dir)
-
-	if options.ls_dir:
-		map_client.list_folders()
-
-	if options.ls_msg is not None:
-		map_client.list_messages(options.ls_msg)
-
-	if options.get_msg is not None:
-		map_client.get_message(options.get_msg)
-
-	if options.get_msg_properties is not None:
-		map_client.get_message_properties(options.get_msg_properties)
+	try:
+		client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+							CLIENT_INTERFACE)
 
-	if options.mark_msg_read is not None:
-		map_client.set_message_property(options.mark_msg_read, "Read", True)
+		print("Creating Session")
 
-	if options.mark_msg_unread is not None:
-		map_client.set_message_property(options.mark_msg_unread, "Read", False)
+		opts = { "Target": "map",
+				"Channel": dbus.Byte(int(options.port)) }
+		if options.source:
+			opts["Source"] = options.source
 
-	if options.mark_msg_deleted is not None:
-		map_client.set_message_property(options.mark_msg_deleted, "Deleted", True)
+		path = client.CreateSession(options.device, opts)
 
-	if options.mark_msg_undeleted is not None:
-		map_client.set_message_property(options.mark_msg_undeleted, "Deleted", False)
+		map_client = MapClient(path, options.verbose)
+		map_client.cmdloop()
 
-	if options.update_inbox:
-		map_client.update_inbox()
+	except (KeyboardInterrupt, SystemExit):
+		pass
+	except Exception as e:
+		print("Error", e)
 
-	mainloop.run()
+	mainloop.quit()
+	mainloop_thread.join()
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/1] test: Make map script a command line client
  2013-01-11 16:44 [PATCH 1/1] test: Make map script a command line client Christian Fetzer
@ 2013-01-11 20:48 ` Marcel Holtmann
  2013-01-13 15:29   ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 4+ messages in thread
From: Marcel Holtmann @ 2013-01-11 20:48 UTC (permalink / raw)
  To: Christian Fetzer; +Cc: linux-bluetooth, Christian Fetzer

Hi Christian,

> Rework the map-client test script into an interactive command line client.
> Now multiple MCE functions can be called in one active session.
> The script also allows to specify all filters or optional parameters including
> auto completion.
> 
> Change-Id: I9c9ede2bc958009c757384177cf06c081d984c98

no Change-Id crap please.

Regards

Marcel



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/1] test: Make map script a command line client
  2013-01-11 20:48 ` Marcel Holtmann
@ 2013-01-13 15:29   ` Luiz Augusto von Dentz
  2013-01-14 10:46     ` Christian Fetzer
  0 siblings, 1 reply; 4+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-13 15:29 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Christian Fetzer, linux-bluetooth@vger.kernel.org,
	Christian Fetzer

Hi Marcel, Christian,

On Fri, Jan 11, 2013 at 10:48 PM, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi Christian,
>
>> Rework the map-client test script into an interactive command line client.
>> Now multiple MCE functions can be called in one active session.
>> The script also allows to specify all filters or optional parameters including
>> auto completion.
>>
>> Change-Id: I9c9ede2bc958009c757384177cf06c081d984c98
>
> no Change-Id crap please.

I wonder if we should complicate more the testing scripts adding such
futures or start working in a proper C tool such as bluetoothctl e.g.
obexctl?

--
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/1] test: Make map script a command line client
  2013-01-13 15:29   ` Luiz Augusto von Dentz
@ 2013-01-14 10:46     ` Christian Fetzer
  0 siblings, 0 replies; 4+ messages in thread
From: Christian Fetzer @ 2013-01-14 10:46 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org

Hi Luiz,

On 01/13/2013 04:29 PM, Luiz Augusto von Dentz wrote:
> I wonder if we should complicate more the testing scripts adding such 
> futures or start working in a proper C tool such as bluetoothctl e.g. 
> obexctl? -- Luiz Augusto von Dentz -- To unsubscribe from this list: 
> send the line "unsubscribe linux-bluetooth" in the body of a message 
> to majordomo@vger.kernel.org More majordomo info at 
> http://vger.kernel.org/majordomo-info.html 

In my opinion it would be nice if the test scripts support most profile 
features that are implemented in bluez/obexd.
That's why I extended the map-client. I tried to keep it as simple as 
possible while focusing also on good testing usability.

The language used for the test scripts / programs doesn't matter that 
much since the scripts are mainly for developers/testers.
Python has some advantages, the scripts can be changed easily while 
working on new features.

However, until some script replacement tool is available, it still would 
make sense to extend/maintain the scripts that are there.

Regards,
Christian

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-01-14 10:46 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-11 16:44 [PATCH 1/1] test: Make map script a command line client Christian Fetzer
2013-01-11 20:48 ` Marcel Holtmann
2013-01-13 15:29   ` Luiz Augusto von Dentz
2013-01-14 10:46     ` Christian Fetzer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox