From 9ed2bbf2be91150b72bf8987bca63f01a8457ab5 Mon Sep 17 00:00:00 2001
From: ugaston <ugaston@localhost>
Date: Mon, 14 Sep 2009 17:20:50 +0000
Subject: [PATCH] First set of External Changelog functional tests
---
opends/tests/staf-tests/shared/functions/utils.xml | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 653 insertions(+), 5 deletions(-)
diff --git a/opends/tests/staf-tests/shared/functions/utils.xml b/opends/tests/staf-tests/shared/functions/utils.xml
index 3a211e6..68a2e67 100755
--- a/opends/tests/staf-tests/shared/functions/utils.xml
+++ b/opends/tests/staf-tests/shared/functions/utils.xml
@@ -242,6 +242,15 @@
</function-arg-description>
<function-arg-property name="type" value="string"/>
</function-arg-def>
+ <function-arg-def name="searchType"
+ type="optional"
+ default="'substring'">
+ <function-arg-description>
+ the type of the search: substring, exact-case-insensitive or
+ exact-case-sensitive
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
<function-arg-def name="knownIssue" type="optional" default="None">
<function-arg-description>
Known issue. Corresponds to an issue number.
@@ -279,13 +288,22 @@
<return>[myRC, myReason]</return>
</sequence>
</if>
+
+ <script>
+ if searchType == 'substring':
+ searchResult = (re.search(searchre, returnString) != None)
+ elif searchType == 'exact-case-sensitive':
+ searchResult = (expectedString == returnString)
+ elif searchType == 'exact-case-insensitive':
+ searchResult = (expectedString.lower() == returnString.lower())
+ </script>
<!-- Search for the expectedString -->
- <if expr='re.search(searchre, returnString) != None'>
+ <if expr='searchResult'>
<sequence>
<message log="1">
- 'Found substring, %s, in the return string' \
- % (expectedString)
+ 'Search type: %s Found substring, %s, in the return string' \
+ % (searchType, expectedString)
</message>
<script>
myRC = 0
@@ -295,8 +313,8 @@
<else>
<sequence>
<message log="1">
- 'Did not find substring, %s, in the return string, %s' \
- % (expectedString, returnString)
+ 'Search type: %s Did not find substring, %s, in the return \
+ string, %s' % (searchType, expectedString, returnString)
</message>
<script>
myRC = 1
@@ -2938,5 +2956,635 @@
</sequence>
</function>
+
+
+ <!-- This function parses an ldif entry -->
+ <function name="parseLdifEntry">
+ <function-prolog>
+ This function parses an ldif entry and returns a dictionary, e.g.:
+ {'dn':['o=example'],'objectclass':['top','organization'],'o':['example']}
+ </function-prolog>
+ <function-map-args>
+ <function-arg-def name="ldifEntry" type="required">
+ <function-arg-description>
+ Ldif entry to parse (single string).
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ </function-map-args>
+ <sequence>
+ <script>
+ parsedEntry = {}
+ prevAttr = None
+ prevVal = None
+
+ for line in ldifEntry.splitlines():
+ notBlank = (len(line.strip()) != 0)
+ if notBlank and (not line.startswith(' ')):
+ # line corresponds to an attr:val
+ attr = line[:line.find(':')].strip().lower()
+ val = line[line.find(':') + 1:].lstrip()
+ if val.startswith(':'):
+ val = val[1:].lstrip()
+ if attr == 'objectclass':
+ val = val.lower()
+
+ if not (attr in parsedEntry.keys()):
+ # This is the first occurrence of this attr
+ parsedEntry[attr] = [val]
+ else:
+ # There is already some value for this attr
+ parsedEntry[attr].append(val)
+
+ prevAttr = attr
+ prevVal = val
+ elif notBlank:
+ # line corresponds to a trailing value
+ parsedEntry[prevAttr].remove(prevVal)
+ val = prevVal + line.lstrip()
+ parsedEntry[prevAttr].append(val)
+ prevVal = val
+ </script>
+
+ <return> parsedEntry </return>
+ </sequence>
+ </function>
+
+
+
+ <!-- This function parses an ldif change -->
+ <function name="parseLdifChange">
+ <function-prolog>
+ This function parses an ldif change and returns a list, e.g.:
+ [ ['replace','l','London'], ['add','description','This is a test'] ]
+ </function-prolog>
+
+ <function-map-args>
+ <function-arg-def name="ldifChange" type="required">
+ <function-arg-description>
+ Ldif change to parse (single string).
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ </function-map-args>
+ <sequence>
+ <script>
+ parsedChange = []
+ mod = []
+ prevAttr = None
+ prevVal = None
+
+ for line in ldifChange.splitlines():
+ notBlank = (len(line.strip()) != 0)
+ if notBlank and (not line.startswith(' ')) and (not line.startswith('-')):
+ # line corresponds to an attr:val
+ attr = line[:line.find(':')].strip().lower()
+ val = line[line.find(':') + 1:].lstrip()
+
+ if val.startswith(':'):
+ val = val[1:].lstrip()
+
+ if attr == 'objectclass':
+ val = val.lower()
+
+ if (prevVal != None) and (attr == prevVal.lower()):
+ # attr represents indeed an attribute type, so we may assume the
+ # mod already has [mod_type,attr_type]
+ mod.append(val)
+ else:
+ # attr represents the mod_type, and val the attr_type
+ mod.append(attr)
+ mod.append(val.lower())
+
+ prevAttr = attr
+ prevVal = val
+ elif notBlank and line.startswith(' '):
+ # line corresponds to a trailing value
+ mod.remove(prevVal)
+ val = prevVal + line.lstrip()
+ mod.append(val)
+ prevVal = val
+ else:
+ # line is empty or line starts with '-'; this means that
+ # the mod is complete, so we can add it to the parsedChange list
+ parsedChange.append(mod)
+ mod = []
+
+ if len(mod) != 0:
+ # add the trailing mod to the parsedChange list
+ parsedChange.append(mod)
+ </script>
+
+ <return> parsedChange </return>
+ </sequence>
+ </function>
+
+ <!-- This function checks the content of an external changelog entry -->
+ <function name="checkChangelogEntry">
+ <function-prolog>
+ This function checks the content of an external changelog entry
+ </function-prolog>
+
+ <function-map-args>
+ <function-arg-def name="location"
+ type="optional"
+ default="STAF_REMOTE_HOSTNAME">
+ <function-arg-description>
+ Location of target host
+ </function-arg-description>
+ <function-arg-property name="type" value="hostname"/>
+ </function-arg-def>
+ <function-arg-def name="dsPath"
+ type="optional"
+ default="'%s/%s' % (DIRECTORY_INSTANCE_BIN,OPENDSNAME)">
+ <function-arg-description>
+ Pathname to installation root
+ </function-arg-description>
+ <function-arg-property name="type" value="filepath"/>
+ </function-arg-def>
+ <function-arg-def name="changelogEntry" type="required">
+ <function-arg-description>
+ External changelog entry (as an output of parseLdifEntry)
+ </function-arg-description>
+ <function-arg-property name="type" value="dictionary"/>
+ </function-arg-def>
+ <function-arg-def name="targetDN" type="required">
+ <function-arg-description>
+ DN of the target entry for the change
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="changeType" type="required">
+ <function-arg-description>
+ Change type (e.g. add, delete, modify)
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="changeTime"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Change time
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="changeNumber"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Changenumber (only for changelog draft-compatible mode)
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="replicationCSN"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Replication CSN
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="targetEntryUUID"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Target entry uuid
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="replicaIdentifier"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Replica Identifier
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="newRDN"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ NewRDN
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="deleteOldRDN"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ DeleteOldRDN
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="newSuperior"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ NewSuperior
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="changes"
+ type="optional"
+ default="None">
+ <function-arg-description>
+ Changes. If changetype == add, changes should be a map, e.g.:
+ {'dn':['o=example'],'objectclass':['top','organization'],'o':['example']}
+ If changetype == modify, changes should be a list, e.g.:
+ [ ['replace','l','London'], ['add','description','This is a test'] ]
+ </function-arg-description>
+ <function-arg-property name="type" value="string"/>
+ </function-arg-def>
+ <function-arg-def name="knownIssue" type="optional" default="None">
+ <function-arg-description>
+ Known issue. Corresponds to an issue number.
+ </function-arg-description>
+ <function-arg-property name="type" value="string" />
+ </function-arg-def>
+ </function-map-args>
+
+ <sequence>
+ <script>
+ myLocation = location
+ myPath = dsPath
+
+ # Mandatory attributes in a changeLogEntry
+ ecl_DN = changelogEntry['dn'][0]
+ ecl_targetDN = changelogEntry['targetdn'][0]
+ ecl_changeType = changelogEntry['changetype'][0]
+ ecl_changeTime = changelogEntry['changetime'][0]
+ ecl_changeNumber = changelogEntry['changenumber'][0]
+
+ # Optional attributes
+ ecl_replicationCSN = None
+ ecl_replicaIdentifier = None
+ ecl_targetEntryUUID = None
+ ecl_newRDN = None
+ ecl_deleteOldRDN = None
+ ecl_newSuperior = None
+ ecl_changes = None
+
+ if 'replicationcsn' in changelogEntry.keys():
+ ecl_replicationCSN = changelogEntry['replicationcsn'][0]
+ if 'replicaidentifier' in changelogEntry.keys():
+ ecl_replicaIdentifier = changelogEntry['replicaidentifier'][0]
+ if 'targetentryuuid' in changelogEntry.keys():
+ ecl_targetEntryUUID = changelogEntry['targetentryuuid'][0]
+ if 'newrdn' in changelogEntry.keys():
+ ecl_newRDN = changelogEntry['newrdn'][0]
+ if 'deleteoldrdn' in changelogEntry.keys():
+ ecl_deleteOldRDN = changelogEntry['deleteoldrdn'][0]
+ if 'newsuperior' in changelogEntry.keys():
+ ecl_newSuperior = changelogEntry['newsuperior'][0]
+ if 'changes' in changelogEntry.keys():
+ ecl_changes = changelogEntry['changes'][0]
+ </script>
+
+ <message>
+ 'checkChangelogEntry: Checking changelog entry %s against expected \
+ values' % ecl_DN
+ </message>
+
+ <message>
+ 'checkChangelogEntry: Checking targetDN'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_targetDN,
+ 'expectedString' : targetDN,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+
+ <message>
+ 'checkChangelogEntry: Checking changeType'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_changeType,
+ 'expectedString' : changeType,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+
+ <if expr="changeTime">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking changeTime'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_changeTime,
+ 'expectedString' : changeTime,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+ </sequence>
+ </if>
+
+ <if expr="changeNumber">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking changeNumber'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_changeNumber,
+ 'expectedString' : changeNumber,
+ 'searchType' : 'exact-case-sensitive'
+ }
+ </call>
+ </sequence>
+ </if>
+
+ <if expr="replicationCSN">
+ <if expr="ecl_replicationCSN">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking replicationCSN'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_replicationCSN,
+ 'expectedString' : replicationCSN,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No replicationCSN could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="replicaIdentifier">
+ <if expr="ecl_replicaIdentifier">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking replicaIdentifier'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_replicaIdentifier,
+ 'expectedString' : replicaIdentifier,
+ 'searchType' : 'exact-case-sensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No replicaIdentifier could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="targetEntryUUID">
+ <if expr="ecl_targetEntryUUID">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking targetEntryUUID'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_targetEntryUUID,
+ 'expectedString' : targetEntryUUID,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No targetEntryUUID could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="newRDN">
+ <if expr="ecl_newRDN">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking newRDN'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_newRDN,
+ 'expectedString' : newRDN,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No newRDN could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="deleteOldRDN">
+ <if expr="ecl_deleteOldRDN">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking deleteOldRDN'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_deleteOldRDN,
+ 'expectedString' : deleteOldRDN,
+ 'searchType' : 'exact-case-sensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No deleteOldRDN could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="newSuperior">
+ <if expr="ecl_newSuperior">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking newSuperior'
+ </message>
+ <call function="'searchString'">
+ { 'returnString' : ecl_newSuperior,
+ 'expectedString' : newSuperior,
+ 'searchType' : 'exact-case-insensitive'
+ }
+ </call>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No newSuperior could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ <if expr="changes">
+ <if expr="ecl_changes">
+ <sequence>
+ <!-- Decode the changes that are encoded in base64 -->
+ <message>
+ 'checkChangelogEntry: Decode external changelog entry changes'
+ </message>
+ <call function="'Base64WithScript'">
+ { 'location' : myLocation,
+ 'dsPath' : myPath,
+ 'subcommand' : 'decode',
+ 'encodedData' : ecl_changes
+ }
+ </call>
+ <!-- STAXResult is not always a list-->
+ <script>
+ try:
+ decodeRC, decodedChanges = STAXResult[0]
+ except AttributeError, details:
+ decodedChanges = 'AttributeError: can not parse STAXResult %s' \
+ % details
+ decodeRC = '1'
+ </script>
+ <message>
+ 'checkChangelogEntry: Decoded changes:\n%s' % decodedChanges
+ </message>
+
+ <message>
+ 'checkChangelogEntry: Checking changes'
+ </message>
+ <if expr="decodeRC == 0 and changeType == 'add'">
+ <!-- If changetype:add, the changes look like a sequence of
+ ! attribute:value, so we may parse them as an ldif entry -->
+ <sequence>
+ <call function="'parseLdifEntry'">
+ { 'ldifEntry' : decodedChanges }
+ </call>
+ <script>
+ ecl_changesMap = STAXResult
+ </script>
+ <message>
+ 'Parsed changelog entry changes: \n%s' % ecl_changesMap
+ </message>
+ <iterate var="attr" in="changes.keys()">
+ <sequence>
+ <script>
+ valueList = changes[attr]
+ ecl_valueList = None
+
+ if attr in ecl_changesMap.keys():
+ ecl_valueList = ecl_changesMap[attr]
+ ecl_valueList.sort()
+ valueList.sort()
+ </script>
+ <if expr="ecl_valueList != None">
+ <sequence>
+ <message>
+ 'checkChangelogEntry: Checking changes: %s' % attr
+ </message>
+ <if expr="valueList == ecl_valueList">
+ <message>
+ 'Found expected values in changes: %s' % valueList
+ </message>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'Expected values %s could not be found in %s' \
+ % (valueList, ecl_valueList)
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No %s could be found in the changes' % attr
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </sequence>
+ </iterate>
+ </sequence>
+
+ <elseif expr="decodeRC == 0">
+ <!-- If changetype:modify, the changes look like a sequence of
+ ! mod_type:attribute
+ ! attribute:value
+ ! so we need to treat them differently -->
+ <sequence>
+ <call function="'parseLdifChange'">
+ { 'ldifChange' : decodedChanges }
+ </call>
+ <script>
+ ecl_changesList = STAXResult
+ </script>
+ <message>
+ 'Parsed changelog entry changes: \n%s' % ecl_changesList
+ </message>
+ <iterate var="mod" in="changes">
+ <sequence>
+ <script>
+ mod_type = mod[0]
+ mod_attr = mod[1]
+ mod_val = None
+ if len(mod) == 3:
+ mod_val = mod[2]
+ </script>
+ <message>
+ 'checkChangelogEntry: Checking changes: %s' % mod
+ </message>
+ <if expr="mod in ecl_changesList">
+ <message>
+ 'Found expected change:\n %s: %s\n %s: %s\n' \
+ % (mod_type, mod_attr, mod_attr, mod_val)
+ </message>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'Expected change %s could not be found in %s'\
+ % (mod, ecl_changesList)
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </sequence>
+ </iterate>
+ </sequence>
+ </elseif>
+ </if>
+
+ </sequence>
+ <else>
+ <sequence>
+ <message log="1" level="'Error'">
+ 'No changes could be found in the changelog entry'
+ </message>
+ <call function="'testFailed'"/>
+ </sequence>
+ </else>
+ </if>
+ </if>
+
+ </sequence>
+ </function>
</stax>
--
Gitblit v1.10.0