コード例 #1
0
ファイル: optimiser.py プロジェクト: wtmmac/smush.py
 def __init__(self, **kwargs):
     # the number of times the _get_command iterator has been run
     self.iterations = 0
     self.files_scanned = 0
     self.files_optimised = 0
     self.bytes_saved = 0
     self.list_only = kwargs.get('list_only')
     self.array_optimised_file = []
     self.quiet = kwargs.get('quiet')
     self.stdout = Scratch()
     self.stderr = Scratch()
コード例 #2
0
ファイル: optimiser.py プロジェクト: beatak/smush.py
 def __init__(self, **kwargs):
     # the number of times the _get_command iterator has been run
     self.iterations = 0
     self.files_scanned = 0
     self.files_optimised = 0
     self.bytes_saved = 0
     self.list_only = kwargs.get('list_only')
     self.array_optimised_file = []
     self.quiet = kwargs.get('quiet')
     self.stdout = Scratch()
     self.stderr = Scratch()
コード例 #3
0
ファイル: smush.py プロジェクト: beatak/smush.py
    def __init__(self, **kwargs):
        self.optimisers = {
            'PNG': OptimisePNG(**kwargs),
            'JPEG': OptimiseJPG(**kwargs),
            'GIF': OptimiseGIF(**kwargs),
            'GIFGIF': OptimiseAnimatedGIF(**kwargs)
        }

        self.__files_scanned = 0
        self.__start_time = time.time()
        self.exclude = {}
        for dir in kwargs.get('exclude'):
            if len(dir) == 0:
                continue
            self.exclude[dir] = True
        self.quiet = kwargs.get('quiet')

        # setup tempfile for stdout and stderr
        self.stdout = Scratch()
        self.stderr = Scratch()
コード例 #4
0
    def __init__(self, **kwargs):
        self.optimisers = {
            #'PNG': OptimisePNG(**kwargs),
            'JPEG': OptimiseJPG(**kwargs),
            #'GIF': OptimiseGIF(**kwargs),
            #'GIFGIF': OptimiseAnimatedGIF(**kwargs)
        }

        self.__files_scanned = 0
        self.__start_time = time.time()
        self.exclude = {}
        for dir in kwargs.get('exclude'):
            if len(dir) == 0:
                continue
            self.exclude[dir] = True
        self.quiet = kwargs.get('quiet')
        self.identify_mime = kwargs.get('identify_mime')

        # setup tempfile for stdout and stderr
        self.stdout = Scratch()
        self.stderr = Scratch()
コード例 #5
0
ファイル: optimiser.py プロジェクト: wtmmac/smush.py
class Optimiser(object):
    """
    Super-class for optimisers
    """

    input_placeholder = "__INPUT__"
    output_placeholder = "__OUTPUT__"

    # string to place between the basename and extension of output images
    output_suffix = "-opt.smush"

    def __init__(self, **kwargs):
        # the number of times the _get_command iterator has been run
        self.iterations = 0
        self.files_scanned = 0
        self.files_optimised = 0
        self.bytes_saved = 0
        self.list_only = kwargs.get('list_only')
        self.array_optimised_file = []
        self.quiet = kwargs.get('quiet')
        self.stdout = Scratch()
        self.stderr = Scratch()

    def __del__(self):
        self.stdout.destruct()
        self.stderr.destruct()

    def set_input(self, input):
        self.iterations = 0
        self.input = input

    def _get_command(self):
        """
        Returns the next command to apply
        """
        command = False

        if self.iterations < len(self.commands):
            command = self.commands[self.iterations]
            self.iterations += 1

        return command

    def _get_output_file_name(self):
        """
        Returns the input file name with Optimiser.output_suffix inserted before the extension
        """
        temp = tempfile.mkstemp(suffix=Optimiser.output_suffix)
        try:
            output_file_name = temp[1]
            os.unlink(output_file_name)
            return output_file_name
        finally:
            os.close(temp[0])

    def __replace_placeholders(self, command, input, output):
        """
        Replaces the input and output placeholders in a string with actual parameter values
        """
        return command.replace(Optimiser.input_placeholder,
                               input).replace(Optimiser.output_placeholder,
                                              output)

    def _keep_smallest_file(self, input, output):
        """
        Compares the sizes of two files, and discards the larger one
        """
        input_size = os.path.getsize(input)
        output_size = os.path.getsize(output)

        # if the image was optimised (output is smaller than input), overwrite the input file with the output
        # file.
        if (output_size > 0 and output_size < input_size):
            try:
                shutil.copyfile(output, input)
                self.files_optimised += 1
                self.bytes_saved += (input_size - output_size)
            except IOError:
                logging.error("Unable to copy %s to %s: %s" %
                              (output, input, IOError))
                sys.exit(1)

        # delete the output file
        os.unlink(output)

    def _is_acceptable_image(self, input):
        """
        Returns whether the input image can be used by a particular optimiser.

        All optimisers are expected to define a variable called 'format' containing the file format
        as returned by 'identify -format %m'
        """
        test_command = 'identify -format %%m "%s"' % input
        args = shlex.split(test_command)

        try:
            retcode = subprocess.call(args,
                                      stdout=self.stdout.opened,
                                      stderr=self.stderr.opened)
        except OSError:
            logging.error("Error executing command %s. Error was %s" %
                          (test_command, OSError))
            sys.exit(1)
        except:
            # most likely no file matched
            if self.quiet == False:
                logging.warning("Cannot identify file.")
            return False
        if retcode != 0:
            if self.quiet == False:
                logging.warning("Cannot identify file.")
            return False
        output = self.stdout.read().strip()
        return output.startswith(self.format)

    def optimise(self):
        """
        Calls the 'optimise_image' method on the object. Tests the 'optimised' file size. If the
        generated file is larger than the original file, discard it, otherwise discard the input file.
        """
        # make sure the input image is acceptable for this optimiser
        if not self._is_acceptable_image(self.input):
            logging.warning("%s is not a valid image for this optimiser" %
                            (self.input))
            return

        self.files_scanned += 1

        while True:
            command = self._get_command()

            if not command:
                break

            output_file_name = self._get_output_file_name()
            command = self.__replace_placeholders(command, self.input,
                                                  output_file_name)
            logging.info("Executing %s" % (command))
            args = shlex.split(command)

            try:
                retcode = subprocess.call(args,
                                          stdout=self.stdout.opened,
                                          stderr=self.stderr.opened)
            except OSError:
                logging.error("Error executing command %s. Error was %s" %
                              (command, OSError))
                sys.exit(1)

            if retcode != 0:
                # gifsicle seems to fail by the file size?
                os.unlink(output_file_name)
            else:
                if self.list_only == False:
                    # compare file sizes if the command executed successfully
                    self._keep_smallest_file(self.input, output_file_name)
                else:
                    self._list_only(self.input, output_file_name)

    def _list_only(self, input, output):
        """
        Always keeps input, but still compares the sizes of two files
        """
        input_size = os.path.getsize(input)
        output_size = os.path.getsize(output)

        if (output_size > 0 and output_size < input_size):
            self.files_optimised += 1
            self.bytes_saved += (input_size - output_size)
            self.array_optimised_file.append(input)

        # delete the output file
        os.unlink(output)
コード例 #6
0
ファイル: optimiser.py プロジェクト: beatak/smush.py
class Optimiser(object):
    """
    Super-class for optimisers
    """

    input_placeholder = "__INPUT__"
    output_placeholder = "__OUTPUT__"

    # string to place between the basename and extension of output images
    output_suffix = "-opt.smush"


    def __init__(self, **kwargs):
        # the number of times the _get_command iterator has been run
        self.iterations = 0
        self.files_scanned = 0
        self.files_optimised = 0
        self.bytes_saved = 0
        self.list_only = kwargs.get('list_only')
        self.array_optimised_file = []
        self.quiet = kwargs.get('quiet')
        self.stdout = Scratch()
        self.stderr = Scratch()

    def __del__(self):
        self.stdout.destruct()
        self.stderr.destruct()

    def set_input(self, input):
        self.iterations = 0
        self.input = input


    def _get_command(self):
        """
        Returns the next command to apply
        """
        command = False
        
        if self.iterations < len(self.commands):
            command = self.commands[self.iterations]
            self.iterations += 1

        return command


    def _get_output_file_name(self):
        """
        Returns the input file name with Optimiser.output_suffix inserted before the extension
        """
        temp = tempfile.mkstemp(suffix=Optimiser.output_suffix)
        output_file_name = temp[1]
        os.unlink(output_file_name)
        return output_file_name


    def __replace_placeholders(self, command, input, output):
        """
        Replaces the input and output placeholders in a string with actual parameter values
        """
        return command.replace(Optimiser.input_placeholder, input).replace(Optimiser.output_placeholder, output)


    def _keep_smallest_file(self, input, output):
        """
        Compares the sizes of two files, and discards the larger one
        """
        input_size = os.path.getsize(input)
        output_size = os.path.getsize(output)

        # if the image was optimised (output is smaller than input), overwrite the input file with the output
        # file.
        if (output_size > 0 and output_size < input_size):
            try:
                shutil.copyfile(output, input)
                self.files_optimised += 1
                self.bytes_saved += (input_size - output_size)
            except IOError:
                logging.error("Unable to copy %s to %s: %s" % (output, input, IOError))
                sys.exit(1)
        
        # delete the output file
        os.unlink(output)
        

    def _is_acceptable_image(self, input):
        """
        Returns whether the input image can be used by a particular optimiser.

        All optimisers are expected to define a variable called 'format' containing the file format
        as returned by 'identify -format %m'
        """
        test_command = 'identify -format %%m "%s"' % input
        args = shlex.split(test_command)

        try:
            retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened)
        except OSError:
            logging.error("Error executing command %s. Error was %s" % (test_command, OSError))
            sys.exit(1)
        except:
            # most likely no file matched
            if self.quiet == False:
                logging.warning("Cannot identify file.")
            return False
        if retcode != 0:
            if self.quiet == False:
                logging.warning("Cannot identify file.")
            return False
        output = self.stdout.read().strip()
        return output.startswith(self.format)


    def optimise(self):
        """
        Calls the 'optimise_image' method on the object. Tests the 'optimised' file size. If the
        generated file is larger than the original file, discard it, otherwise discard the input file.
        """
        # make sure the input image is acceptable for this optimiser
        if not self._is_acceptable_image(self.input):
            logging.warning("%s is not a valid image for this optimiser" % (self.input))
            return

        self.files_scanned += 1

        while True:
            command = self._get_command()

            if not command:
                break

            output_file_name = self._get_output_file_name()
            command = self.__replace_placeholders(command, self.input, output_file_name)
            logging.info("Executing %s" % (command))
            args = shlex.split(command)
            
            try:
                retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened)
            except OSError:
                logging.error("Error executing command %s. Error was %s" % (command, OSError))
                sys.exit(1)

            if retcode != 0:
                # gifsicle seems to fail by the file size?
                os.unlink(output_file_name)
            else :
                if self.list_only == False:
                    # compare file sizes if the command executed successfully
                    self._keep_smallest_file(self.input, output_file_name)
                else:
                    self._list_only(self.input, output_file_name)


    def _list_only(self, input, output):
        """
        Always keeps input, but still compares the sizes of two files
        """
        input_size = os.path.getsize(input)
        output_size = os.path.getsize(output)

        if (output_size > 0 and output_size < input_size):
            self.files_optimised += 1
            self.bytes_saved += (input_size - output_size)
            self.array_optimised_file.append(input)
        
        # delete the output file
        os.unlink(output)
コード例 #7
0
# 容差参数,主要用于让被抓物体固定不动后再去抓
ERROR = 0.005
MAX = 8

# 初始化
data = '' 
lastdata = ''
ip_port = ('10.0.0.1', 9999) # 监听9999端口
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) # UDP协议
sk.bind(ip_port)
st = threading.Thread(target = listen, args = ())
st.start()

count = MAX
mutex = threading.Lock()
scratch = Scratch()
capture = cv2.VideoCapture(0)
ratio = real_width / img_width
cascade = cv2.CascadeClassifier('/Users/guliqi/Desktop/Classifier/xml/cascade.xml')

while True:
	ret, frame = capture.read()

	if data != '':
		if data.upper() == b'COALA': # 抓考拉
			gray = frame.cvtColor(frame, cv2.COLOR_BGR2GRAY)
			kolas = cascade.detectMultiScale(gray, 2.4, 7)
			objects = []
			for (x, y, w, h) in kolas:
				frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
				objects.append((x + w / 2, y + h / 2))
コード例 #8
0
ファイル: parsing.py プロジェクト: penguintutor/scratchpy
 def setUp(self):
     self.client = Scratch()
コード例 #9
0
ファイル: parsing.py プロジェクト: penguintutor/scratchpy
class ParsingTests(unittest.TestCase):
    """
    Must have Scratch running with remote sensor connections enabled
    """
    def setUp(self):
        self.client = Scratch()

    def test_broadcast(self):
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x0fbroadcast "a b"'), 
            ('broadcast', 'a b')
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x13broadcast """a b"""'),
            ('broadcast', '"a b"')
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x17broadcast """""a b"""""'),
            ('broadcast', '""a b""')
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x13broadcast """a"" b"'),
            ('broadcast', '"a" b')
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x17broadcast """a"" ""b"""'),
            ('broadcast', '"a" "b"')
        )

    def test_sensorupdate(self):
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x14sensor-update "a" 0 '), 
            ('sensor-update', {'a': 0})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x16sensor-update "a" "c" '), 
            ('sensor-update', {'a': 'c'})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x16sensor-update "a b" 0 '), 
            ('sensor-update', {'a b': 0})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x1asensor-update """a b""" 0 '), 
            ('sensor-update', {'"a b"': 0})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x003sensor-update """a b""" "hello hi" "a" "c" "a b" 0 '), 
            ('sensor-update', {'"a b"': 'hello hi', 'a': 'c', 'a b': 0})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x1esensor-update """a"" ""b""" 0 '), 
            ('sensor-update', {'"a" "b"': 0})
        )
        self.assertEquals(
            self.client._parse('\x00\x00\x00*sensor-update """a"" ""b""" """c"" ""d""" '), 
            ('sensor-update', {'"a" "b"': '"c" "d"'})
        )

    def test_is_msg(self):
        self.assertTrue(self.client._is_msg('\x00\x00\x00\x14sensor-update "a" 0 '))
        self.assertTrue(self.client._is_msg('\x00\x00\x00\x0ebroadcast "hi"'))
        self.assertFalse(self.client._is_msg('\x00\x00\x00\x14sensor-update "a"'))
        self.assertFalse(self.client._is_msg('\x00\x00\x00\x14benbor-update "a" 0 '))
        self.assertFalse(self.client._is_msg(''))
        self.assertFalse(self.client._is_msg(None))
        self.assertFalse(self.client._is_msg('\x00\x00\x00\x00'))
        self.assertFalse(self.client._is_msg('\x00\x00\x00'))
    
    def test_escape(self):
        self.assertEquals(self.client._escape(''), '')
        self.assertEquals(self.client._escape('"a"'), '""a""')

    def test_unescape(self):
        self.assertEquals(self.client._unescape(''), '')
        self.assertEquals(self.client._unescape('a'), 'a')
        self.assertEquals(self.client._unescape('""a""'), '"a"')
        self.assertEquals(self.client._unescape(0), 0)
コード例 #10
0
ファイル: smush.py プロジェクト: rtCamp/imagy
class Smush():
    def __init__(self, **kwargs):
        self.optimisers = {
            'PNG': OptimisePNG(**kwargs),
            'JPEG': OptimiseJPG(**kwargs),
            'GIF': OptimiseGIF(**kwargs),
            'GIFGIF': OptimiseAnimatedGIF(**kwargs)
        }

        self.__files_scanned = 0
        self.__start_time = time.time()
        self.exclude = {}
        for dir in kwargs.get('exclude', []):
            if len(dir) == 0:
                continue
            self.exclude[dir] = True
        self.quiet = kwargs.get('quiet')
        self.identify_mime = kwargs.get('identify_mime')

        # setup tempfile for stdout and stderr
        self.stdout = Scratch()
        self.stderr = Scratch()

    def __del__(self):
        pass
        #self.stdout.destruct()
        #self.stderr.destruct()

    def smush(self, file):
        """
        Optimises a file
        """
        key = self.__get_image_format(file)

        if key in self.optimisers:
            #logging.info('optimising file %s' % (file))
            self.__files_scanned += 1
            self.optimisers[key].set_input(file)
            self.optimisers[key].optimise()
            
    __smush = smush

    def process(self, dir, recursive):
        """
        Iterates through the input directory optimising files
        """
        if recursive:
            self.__walk(dir, self.__smush)
        else:
            if os.path.isdir(dir):
                dir = os.path.abspath(dir)
                for file in os.listdir(dir):
                    if self.__checkExclude(file):
                        continue
                        
                    if self.identify_mime:
                        import mimetypes
                        (type,encoding) = mimetypes.guess_type(file)
                        if type and (type[:5] != "image"):
                            continue

                    self.__smush(os.path.join(dir, file))
            elif os.path.isfile(dir):
                self.__smush(dir)


    def __walk(self, dir, callback):
        """ Walks a directory, and executes a callback on each file """
        dir = os.path.abspath(dir)
        logging.info('walking %s' % (dir))
        for file in os.listdir(dir):
            if self.__checkExclude(file):
                continue
            
            if self.identify_mime:
                import mimetypes
                (type,encoding) = mimetypes.guess_type(file)        
                if type and (type[:5] != "image"):
                    continue

            nfile = os.path.join(dir, file)
            callback(nfile)
            if os.path.isdir(nfile):
                self.__walk(nfile, callback)


    def __get_image_format(self, input):
        """
        Returns the image format for a file.
        """
        test_command = 'identify -format %%m "%s"' % input
        args = shlex.split(test_command.encode('utf8'))

        try:
            retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened)
            if retcode != 0:
                if self.quiet == False:
                    logging.warning(self.stderr.read().strip())
                return False

        except OSError:
            logging.error('Error executing command %s. Error was %s' % (test_command, OSError))
            sys.exit(1)
        except:
            # most likely no file matched
            if self.quiet == False:
                logging.warning('Cannot identify file.')
            return False

        return self.stdout.read().strip()[:6]


    def stats(self):
        output = []
        output.append('\n%d files scanned:' % (self.__files_scanned))
        arr = []

        for key, optimiser in self.optimisers.iteritems():
            # divide optimiser.files_optimised by 2 for each optimiser since each optimised file
            # gets counted twice
            output.append('    %d %ss optimised out of %d scanned. Saved %dkb' % (
                    optimiser.files_optimised // 2,
                    key, 
                    optimiser.files_scanned, 
                    optimiser.bytes_saved / 1024))
            arr.extend(optimiser.array_optimised_file)

        if (len(arr) != 0):
            output.append('Modified files:')
            for filename in arr:
                output.append('    %s' % filename)
        output.append('Total time taken: %.2f seconds' % (time.time() - self.__start_time))
        return {'output': "\n".join(output), 'modified': arr}


    def __checkExclude(self, file):
        if file in self.exclude:
            logging.info('%s is excluded.' % (file))
            return True
        return False
コード例 #11
0
class Smush():
    def __init__(self, **kwargs):
        self.optimisers = {
            #'PNG': OptimisePNG(**kwargs),
            'JPEG': OptimiseJPG(**kwargs),
            #'GIF': OptimiseGIF(**kwargs),
            #'GIFGIF': OptimiseAnimatedGIF(**kwargs)
        }

        self.__files_scanned = 0
        self.__start_time = time.time()
        self.exclude = {}
        for dir in kwargs.get('exclude'):
            if len(dir) == 0:
                continue
            self.exclude[dir] = True
        self.quiet = kwargs.get('quiet')
        self.identify_mime = kwargs.get('identify_mime')

        # setup tempfile for stdout and stderr
        self.stdout = Scratch()
        self.stderr = Scratch()

    def __del__(self):
        self.stdout.destruct()
        self.stderr.destruct()

    def __smush(self, file):
        """
        Optimises a file
        """
        key = self.__get_image_format(file)

        if key in self.optimisers:
            logging.info('optimising file %s' % (file))
            self.__files_scanned += 1
            self.optimisers[key].set_input(file)
            self.optimisers[key].optimise()


    def process(self, dir, recursive):
        """
        Iterates through the input directory optimising files
        """
        if recursive:
            self.__walk(dir, self.__smush)
        else:
            if os.path.isdir(dir):
                dir = os.path.abspath(dir)
                for file in os.listdir(dir):
                    if self.__checkExclude(file):
                        continue
                        
                    if self.identify_mime:
                        import mimetypes
                        (type,encoding) = mimetypes.guess_type(file)
                        if type and (type[:5] != "image"):
                            continue

                    self.__smush(os.path.join(dir, file))
            elif os.path.isfile(dir):
                self.__smush(dir)


    def __walk(self, dir, callback):
        """ Walks a directory, and executes a callback on each file """
        dir = os.path.abspath(dir)
        logging.info('walking %s' % (dir))
        for file in os.listdir(dir):
            if self.__checkExclude(file):
                continue
            
            if self.identify_mime:
                import mimetypes
                (type,encoding) = mimetypes.guess_type(file)        
                if type and (type[:5] != "image"):
                    continue

            nfile = os.path.join(dir, file)
            callback(nfile)
            if os.path.isdir(nfile):
                self.__walk(nfile, callback)


    def __get_image_format(self, input):
        """
        Returns the image format for a file.
        """
        test_command = 'identify -format %%m "%s"' % input
        args = shlex.split(test_command)

        try:
            retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened)
            if retcode != 0:
                if self.quiet == False:
                    logging.warning(self.stderr.read().strip())
                return False

        except OSError:
            logging.error('Error executing command %s. Error was %s' % (test_command, OSError))
            sys.exit(1)
        except:
            # most likely no file matched
            if self.quiet == False:
                logging.warning('Cannot identify file.')
            return False

        return self.stdout.read().strip()[:6]


    def stats(self):
        output = []
        output.append('\n%d files scanned:' % (self.__files_scanned))
        arr = []

        for key, optimiser in self.optimisers.items():
            # divide optimiser.files_optimised by 2 for each optimiser since each optimised file
            # gets counted twice
            output.append('    %d %ss optimised out of %d scanned. Saved %dkb' % (
                    optimiser.files_optimised // 2,
                    key, 
                    optimiser.files_scanned, 
                    optimiser.bytes_saved / 1024))
            arr.extend(optimiser.array_optimised_file)

        if (len(arr) != 0):
            output.append('Modified files:')
            for filename in arr:
                output.append('    %s' % filename)
        output.append('Total time taken: %.2f seconds' % (time.time() - self.__start_time))
        return {'output': "\n".join(output), 'modified': arr}


    def __checkExclude(self, file):
        if file in self.exclude:
            logging.info('%s is excluded.' % (file))
            return True
        return False
コード例 #12
0
from scratch import Scratch
from scratch.translater import translate

sc = Scratch('Truth 21w4a.sb3')
print(translate(sc.analyze()['blockStat'], 'cn-lang.json'))
コード例 #13
0
 def setUp(self):
     self.client = Scratch()
コード例 #14
0
class ParsingTests(unittest.TestCase):
    """
    Must have Scratch running with remote sensor connections enabled
    """
    def setUp(self):
        self.client = Scratch()

    def test_broadcast(self):
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x0fbroadcast "a b"'),
            ('broadcast', 'a b'))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x13broadcast """a b"""'),
            ('broadcast', '"a b"'))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x17broadcast """""a b"""""'),
            ('broadcast', '""a b""'))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x13broadcast """a"" b"'),
            ('broadcast', '"a" b'))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x17broadcast """a"" ""b"""'),
            ('broadcast', '"a" "b"'))

    def test_sensorupdate(self):
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x14sensor-update "a" 0 '),
            ('sensor-update', {
                'a': 0
            }))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x16sensor-update "a" "c" '),
            ('sensor-update', {
                'a': 'c'
            }))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x16sensor-update "a b" 0 '),
            ('sensor-update', {
                'a b': 0
            }))
        self.assertEquals(
            self.client._parse('\x00\x00\x00\x1asensor-update """a b""" 0 '),
            ('sensor-update', {
                '"a b"': 0
            }))
        self.assertEquals(
            self.client._parse(
                '\x00\x00\x003sensor-update """a b""" "hello hi" "a" "c" "a b" 0 '
            ), ('sensor-update', {
                '"a b"': 'hello hi',
                'a': 'c',
                'a b': 0
            }))
        self.assertEquals(
            self.client._parse(
                '\x00\x00\x00\x1esensor-update """a"" ""b""" 0 '),
            ('sensor-update', {
                '"a" "b"': 0
            }))
        self.assertEquals(
            self.client._parse(
                '\x00\x00\x00*sensor-update """a"" ""b""" """c"" ""d""" '),
            ('sensor-update', {
                '"a" "b"': '"c" "d"'
            }))

    def test_is_msg(self):
        self.assertTrue(
            self.client._is_msg('\x00\x00\x00\x14sensor-update "a" 0 '))
        self.assertTrue(self.client._is_msg('\x00\x00\x00\x0ebroadcast "hi"'))
        self.assertFalse(
            self.client._is_msg('\x00\x00\x00\x14sensor-update "a"'))
        self.assertFalse(
            self.client._is_msg('\x00\x00\x00\x14benbor-update "a" 0 '))
        self.assertFalse(self.client._is_msg(''))
        self.assertFalse(self.client._is_msg(None))
        self.assertFalse(self.client._is_msg('\x00\x00\x00\x00'))
        self.assertFalse(self.client._is_msg('\x00\x00\x00'))

    def test_escape(self):
        self.assertEquals(self.client._escape(''), '')
        self.assertEquals(self.client._escape('"a"'), '""a""')

    def test_unescape(self):
        self.assertEquals(self.client._unescape(''), '')
        self.assertEquals(self.client._unescape('a'), 'a')
        self.assertEquals(self.client._unescape('""a""'), '"a"')
        self.assertEquals(self.client._unescape(0), 0)