Changeset 5809

Show
Ignore:
Timestamp:
08/16/10 20:17:58 (3 years ago)
Author:
kosma
Message:

_fields.py: Working implementation of OptionsField?, with tests.

Location:
umpa/branches/protocols
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • umpa/branches/protocols/tests/a_unit/test_protocols/test__fields.py

    r5808 r5809  
    741741        py.test.raises(UMPAException, f.fillout) 
    742742 
     743class TestOptionsField(TestField): 
     744    cls_field = OptionsField 
     745 
     746    def test_init(self): 
     747        f = self.cls_field('options') 
     748        assert f.bits == 0 
     749 
     750    def test_set(self): 
     751        f = self.cls_field('options') 
     752 
     753        f.set(None) 
     754        assert f._value == None 
     755 
     756        f.set('payload') 
     757        assert len(f._value) == 1 
     758        assert f._value[0].get() == 'payload' 
     759 
     760        f.set( [ 'payload' ] ) 
     761        assert len(f._value) == 1 
     762        assert f._value[0].get() == 'payload' 
     763 
     764        f.set( [ (1, ), (2, ''), (3, 'foo'), 'bar', None ] ) 
     765        assert len(f._value) == 5 
     766        assert f._value[0].get() == (1, '') 
     767        assert f._value[1].get() == (2, '') 
     768        assert f._value[2].get() == (3, 'foo') 
     769        assert f._value[3].get() == 'bar' 
     770        assert f._value[4].get() == None 
     771 
     772    def test_get(self): 
     773        f = self.cls_field('options') 
     774        assert f.get() is None 
     775 
     776        f = self.cls_field('options', 'payload') 
     777        assert f.get() == [ 'payload' ] 
     778 
     779        f = self.cls_field('options', [ 'payload' ]) 
     780        assert f.get() == [ 'payload' ] 
     781 
     782        f = self.cls_field('options', [ (1,), (2, ), (3, ''), (4, 'foo'), 'bar', None ] ) 
     783        assert f.get() == [ (1, ''), (2, ''), (3, ''), (4, 'foo'), 'bar', None ] 
     784 
     785    def test_clear(self): 
     786        f = self.cls_field('options', 'payload') 
     787        assert f._value is not None 
     788        assert f.get() is not None 
     789        f.clear() 
     790        assert f._value is None 
     791        assert f.get() is None 
     792 
     793    def test_raw_value(self): 
     794        f = self.cls_field('foobar') 
     795     
     796        f.set('test') 
     797        assert f._raw_value() == 0x74657374 
     798        assert f.bits == 32 
     799 
     800        f.set([ (1,) ]) 
     801        assert f._raw_value() == 0x01 
     802        assert f.bits == 8 
     803 
     804        f.set([ (5, 'test') ]) 
     805        assert f._raw_value() == 0x050674657374 
     806        assert f.bits == 48 
     807 
     808    def test_fillout(self): 
     809        f = self.cls_field('options') 
     810        assert f.fillout() == 0 
     811        assert f.bits == 0 
     812 
    743813class TestOptionField(TestField): 
    744814    cls_field = OptionField 
     
    746816    def test_init(self): 
    747817        f = self.cls_field('foobar') 
    748         assert f.auto is False 
     818        assert f.auto is True 
    749819 
    750820        f = self.cls_field('foobar', 'xxx') 
    751821        assert f.get() == 'xxx' 
    752822 
     823    def test_set(self): 
     824        f = self.cls_field('foobar') 
     825         
     826        f.set((1,)) 
     827        assert f._value == (1, '') 
     828        f.set((1, '')) 
     829        assert f._value == (1, '') 
     830        f.set((1, 'foo')) 
     831        assert f._value == (1, 'foo') 
     832 
     833        f.set((2, )) 
     834        assert f._value == (2, '') 
     835        f.set((2, '')) 
     836        assert f._value == (2, '') 
     837        f.set((2, 'bar')) 
     838        assert f._value == (2, 'bar') 
     839 
    753840    def test_get(self): 
    754841        f = self.cls_field('foobar') 
     
    758845        assert f.get() == 'text' 
    759846 
    760         f = self.cls_field('foobar', (0,)) 
    761         assert f.get() == (0,) 
    762  
    763     def test_set(self): 
    764         f = self.cls_field('foobar') 
    765          
    766         f.set((1,)) 
    767         assert f.get() == (1,) 
    768         f.set((2, 'x')) 
    769         assert f.get() == (2, 'x') 
     847        f = self.cls_field('foobar', (4, 'test')) 
     848        assert f.get() == (4, 'test') 
    770849 
    771850    def test_clear(self): 
     
    773852        f.set('foobarfoobar') 
    774853        assert f.get() == 'foobarfoobar' 
    775         assert f.bits == 96 
    776854        f.clear() 
    777855        assert f.get() is None 
    778         assert f.bits == 0 
    779856 
    780857    def test_raw_value(self): 
    781858        f = self.cls_field('foobar') 
    782  
     859     
    783860        f.set('test') 
    784861        assert f._raw_value() == 0x74657374 
     
    798875 
    799876        f = self.cls_field('foobar') 
    800         py.test.raises(UMPAException, f.fillout) 
     877        assert f.fillout() == 0x01 
  • umpa/branches/protocols/umit/umpa/protocols/_fields.py

    r5808 r5809  
    950950        return str_to_bits(self._value) 
    951951 
     952class OptionsField(Field): 
     953    """ 
     954    A field holding IP/TCP options. This field contains a list of zero or more 
     955    Fields in a format understood by the OptionField class. Example: 
     956 
     957        [ (4, 'foo'), '\4\5bar', (5, ), (0, ) ] 
     958    """ 
     959 
     960    bits = 0    # dynamic, changes depending on the content 
     961 
     962    def _is_valid(self, value): 
     963        """ 
     964        Validate each value independently; return True only if all pass. 
     965        """ 
     966 
     967        if value is None: 
     968            return True 
     969        of = OptionField('option') 
     970        for opt in value: 
     971            if not of._is_valid(opt): 
     972                return False 
     973        return True 
     974 
     975    def set(self, value): 
     976        """ 
     977        Set the field value. 
     978        """ 
     979 
     980        # allow the user to set a single raw string value 
     981        if isinstance(value, str): 
     982            value = [value] 
     983 
     984        super(OptionsField, self).set(value) 
     985 
     986        if self._value is None or len(value) == 0: 
     987            self.clear() 
     988            return 
     989 
     990        # the above sets _value to the user-supplied list; convert it to  
     991        # a list of OptionField objects 
     992        fields = [] 
     993        for opt in value: 
     994            fields.append(OptionField('option', opt)) 
     995        self._value = fields 
     996 
     997    def get(self): 
     998        """ 
     999        Get the field value. 
     1000        """ 
     1001 
     1002        if self._value is None: 
     1003            return None 
     1004 
     1005        # build a list of values and return it to the user 
     1006        values = [] 
     1007        for field in self._value: 
     1008            values.append(field.get()) 
     1009        return values 
     1010 
     1011    def _generate_value(self): 
     1012        """ 
     1013        No options are included by default. 
     1014        """ 
     1015 
     1016        return [] 
     1017 
     1018    def _raw_value(self): 
     1019        """ 
     1020        Convert the value to the raw mode and update bits field. 
     1021        """ 
     1022 
     1023        raw = 0 
     1024        bits = 0 
     1025 
     1026        for field in self._value: 
     1027            field_raw = field.fillout() 
     1028            raw <<= field.bits 
     1029            raw |= field_raw 
     1030            bits += field.bits 
     1031 
     1032        self.bits = bits 
     1033        return raw 
     1034 
    9521035class OptionField(Field): 
    9531036    """ 
     
    9551038 
    9561039    1. A (option-kind, option-value) tuple. The option-length octet will be 
    957        generated automatically. The option-value should be a raw string value. 
    958        Options 0 and 1 don't have option-value. 
     1040       generated automatically. The option-value should be a raw string value; 
     1041       if it's not supplied, an empty value will be used. 
    9591042    2. A string value which will be inserted as-is without any validation. 
    9601043    """ 
    9611044 
    9621045    bits = 0    # dynamic, changes depending on the content 
     1046    auto = True 
    9631047 
    9641048    def _is_valid(self, value): 
    9651049        """ 
    966         Ensure that the value is either a string or a valid tuple. 
     1050        Ensure that the value is either a string or a non-empty sequence. 
    9671051        """ 
    9681052 
     
    9711055        if len(value) == 0: 
    9721056            return False 
    973         if value[0] in (0, 1): 
    974             return True 
    975         if len(value) == 1: 
    976             return False 
    977         if not isinstance(value[1], str): 
    978             return False 
    9791057        return True 
    9801058 
    9811059    def set(self, value): 
    9821060        """ 
    983         Calculate the bit length after setting the value. 
    984         """ 
    985  
     1061        Canonicalize the field value and recalculate bit length. 
     1062        """ 
     1063 
     1064        # validate and set value 
    9861065        super(OptionField, self).set(value) 
    987         if value is None: 
    988             self.bits = 0 
    989         elif isinstance(value, str): 
    990             # use a raw value as-is 
    991             self.bits = BYTE * len(value) 
    992         else: 
    993             if value[0] in (0, 1): 
    994                 # options 0 and 1 have no option-length field 
    995                 self.bits = BYTE 
    996             else: 
    997                 self.bits = BYTE * (2 + len(value[1])) 
     1066 
     1067        # canonicalize value field: always store as ( kind, value ) pair 
     1068        if self._value is not None and len(self._value) < 2: 
     1069            self._value = ( self._value[0], '' ) 
    9981070 
    9991071    def clear(self): 
    10001072        """ 
     1073        Clear the field. 
     1074         
    10011075        Reset the bit length after clearing the value. 
    10021076        """ 
     
    10071081    def _raw_value(self): 
    10081082        """ 
    1009         Convert the value to the raw mode. 
    1010         """ 
    1011  
     1083        Convert the value to the raw mode and update bits field. 
     1084        """ 
     1085 
     1086        # generate raw value 
    10121087        if isinstance(self._value, str): 
    10131088            data = self._value 
     
    10171092            else: 
    10181093                data = "%c%c%s" % ( self._value[0], len(self._value[1]) + 2, 
    1019                                     self._value[1] ) 
     1094                                     self._value[1] ) 
     1095 
     1096        # update field length 
     1097        self.bits = BYTE * len(data) 
    10201098 
    10211099        return str_to_bits(data) 
     1100 
     1101    def _generate_value(self): 
     1102        """ 
     1103        Return a NOP option by default. 
     1104        """ 
     1105 
     1106        return (1, '')