autoproj-jenkins 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +41 -0
  6. data/Rakefile +11 -0
  7. data/autoproj-jenkins.gemspec +28 -0
  8. data/lib/autoproj/cli/jenkins.rb +100 -0
  9. data/lib/autoproj/cli/main_jenkins.rb +123 -0
  10. data/lib/autoproj/cli/test_postprocessing.rb +85 -0
  11. data/lib/autoproj/jenkins/credentials.rb +67 -0
  12. data/lib/autoproj/jenkins/exceptions.rb +7 -0
  13. data/lib/autoproj/jenkins/relativize.rb +74 -0
  14. data/lib/autoproj/jenkins/render_template.rb +92 -0
  15. data/lib/autoproj/jenkins/server.rb +79 -0
  16. data/lib/autoproj/jenkins/templates/abort-if-upstream-failed.pipeline.erb +18 -0
  17. data/lib/autoproj/jenkins/templates/bootstrap.pipeline.erb +27 -0
  18. data/lib/autoproj/jenkins/templates/buildconf-Gemfile +3 -0
  19. data/lib/autoproj/jenkins/templates/buildconf-config.yml.erb +12 -0
  20. data/lib/autoproj/jenkins/templates/buildconf-vagrant-Gemfile +4 -0
  21. data/lib/autoproj/jenkins/templates/buildconf.pipeline.erb +43 -0
  22. data/lib/autoproj/jenkins/templates/buildconf.xml.erb +24 -0
  23. data/lib/autoproj/jenkins/templates/handle-downstream.pipeline.erb +20 -0
  24. data/lib/autoproj/jenkins/templates/import-archive.pipeline.erb +2 -0
  25. data/lib/autoproj/jenkins/templates/import-git.pipeline.erb +16 -0
  26. data/lib/autoproj/jenkins/templates/import-svn.pipeline.erb +13 -0
  27. data/lib/autoproj/jenkins/templates/jenkins_dependency_overrides.rb.erb +5 -0
  28. data/lib/autoproj/jenkins/templates/package-Gemfile +2 -0
  29. data/lib/autoproj/jenkins/templates/package.pipeline.erb +102 -0
  30. data/lib/autoproj/jenkins/templates/package.xml.erb +23 -0
  31. data/lib/autoproj/jenkins/templates/setup-git-credentials.pipeline.erb +8 -0
  32. data/lib/autoproj/jenkins/templates/wait-upstream.pipeline.erb +160 -0
  33. data/lib/autoproj/jenkins/test_format_converters/boost-test.xsl +347 -0
  34. data/lib/autoproj/jenkins/updater.rb +214 -0
  35. data/lib/autoproj/jenkins/version.rb +5 -0
  36. data/lib/autoproj/jenkins.rb +19 -0
  37. data/lib/autoproj-jenkins.rb +8 -0
  38. metadata +179 -0
@@ -0,0 +1,160 @@
1
+ def isUpstreamOK(jobName, buildId)
2
+ {
3
+ def job = Jenkins.instance.getItem(jobName)
4
+ if (!job)
5
+ {
6
+ error("cannot find upstream job ${jobName}")
7
+ }
8
+
9
+ def build = job.getBuild(buildId.toString())
10
+
11
+ def result = build.getResult()
12
+ if (result)
13
+ {
14
+ if (result == Result.SUCCESS || result == Result.UNSTABLE)
15
+ {
16
+ return 'OK'
17
+ }
18
+ else
19
+ {
20
+ return 'FAILED'
21
+ }
22
+ }
23
+ else
24
+ {
25
+ return 'IN_PROGRESS'
26
+ }
27
+ }
28
+
29
+ def getTriggerBuild(currentBuild)
30
+ {
31
+ def triggerBuild = currentBuild.rawBuild.getCause(hudson.model.Cause$UpstreamCause)
32
+ if (triggerBuild) {
33
+ return [triggerBuild.getUpstreamProject(), triggerBuild.getUpstreamBuild()]
34
+ }
35
+ }
36
+
37
+ def findBuildTriggeredBy(job, triggerJob, triggerBuild)
38
+ {
39
+ def jobBuilds = job.getBuilds()
40
+ for (build in jobBuilds)
41
+ {
42
+ def buildCause = build.getCause(hudson.model.Cause$UpstreamCause)
43
+ if (buildCause)
44
+ {
45
+ def causeJob = buildCause.getUpstreamProject()
46
+ def causeBuild = buildCause.getUpstreamBuild()
47
+ if (causeJob == triggerJob && causeBuild == triggerBuild)
48
+ {
49
+ return build.getNumber()
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ def getUpstreamBuilds(upstreamJobNames, triggerJob, triggerBuild)
56
+ {
57
+ def upstreamBuilds = []
58
+
59
+ // Iterate list -- NOTE: we cannot use groovy style or even modern java style iteration
60
+ for (jobName in upstreamJobNames)
61
+ {
62
+ if (jobName == triggerJob)
63
+ {
64
+ echo "upstream build: ${jobName}#${triggerBuild}"
65
+ upstreamBuilds << [jobName, triggerBuild]
66
+ }
67
+ else
68
+ {
69
+ def job = Jenkins.instance.getItem(jobName)
70
+ def matchingBuild = findBuildTriggeredBy(job, triggerJob, triggerBuild)
71
+ if (!matchingBuild)
72
+ {
73
+ if (triggerJob) {
74
+ echo "no build from ${jobName} has been triggered by ${triggerJob}#${triggerBuild}, using last successful build"
75
+ }
76
+ else {
77
+ echo "manual build trigger, using last successful build for ${jobName}"
78
+ }
79
+ matchingBuild = job.getLastSuccessfulBuild().getNumber()
80
+ }
81
+ echo "upstream build: ${jobName}#${matchingBuild}"
82
+ upstreamBuilds << [jobName, matchingBuild]
83
+ }
84
+ }
85
+ return upstreamBuilds
86
+ }
87
+
88
+ def waitForUpstreamBuilds(upstreamBuilds)
89
+ {
90
+ def waitContinue = true;
91
+ while(waitContinue)
92
+ {
93
+ waitContinue = false;
94
+
95
+ // Iterate list -- NOTE: we cannot use groovy style or even modern java style iteration
96
+ for(entry in upstreamBuilds) {
97
+ def upstreamJobName = entry[0]
98
+ def upstreamBuildId = entry[1]
99
+ def status = isUpstreamOK(upstreamJobName, upstreamBuildId)
100
+ if (status == 'IN_PROGRESS')
101
+ {
102
+ echo "waiting for job ${upstreamJobName}#${upstreamBuildId} to finish"
103
+ waitContinue = true
104
+ sleep time: 1, units: 'SECONDS'
105
+ }
106
+ else if (status == 'FAILED')
107
+ {
108
+ echo "${upstreamJobName}#${upstreamBuildId} did not finish successfully, aborting this build"
109
+ currentBuild.result = 'NOT_BUILT';
110
+ return false;
111
+ }
112
+ }
113
+ }
114
+ return true
115
+
116
+ }
117
+
118
+ def makeUpstreamArtifactImporters(autoproj, fullWorkspaceDir, upstreamDir,
119
+ upstreamJobNames, upstreamPrefixes, upstreamBuilds)
120
+ {
121
+ def fullUpstreamDir = "${fullWorkspaceDir}/${upstreamDir}"
122
+ dir(upstreamDir) { deleteDir() }
123
+
124
+ def upstreamArtifactImporters = [:]
125
+ for (jobIndex = 0; jobIndex < upstreamJobNames.size(); ++jobIndex) {
126
+ def jobName = upstreamJobNames[jobIndex]
127
+ def fullPrefix = upstreamPrefixes[jobIndex]
128
+ def buildId = upstreamBuilds[jobIndex][1]
129
+ def relativePrefix = Paths.get(fullWorkspaceDir).relativize(Paths.get(fullPrefix)).toString()
130
+ upstreamArtifactImporters[jobName] = {
131
+ dir(upstreamDir) {
132
+ step ([$class: 'CopyArtifact',
133
+ projectName: jobName,
134
+ filter: "${jobName}-prefix.zip",
135
+ selector: [$class: 'SpecificBuildSelector', buildNumber: buildId.toString()]])
136
+ dir(jobName) {
137
+ unzip zipFile: "${fullUpstreamDir}/${jobName}-prefix.zip"
138
+ sh "${autoproj} jenkins relativize ./ '@WORKSPACE_ROOT@' '${fullWorkspaceDir}'"
139
+ }
140
+ }
141
+ dir(relativePrefix) {
142
+ sh "rsync '${fullUpstreamDir}/${jobName}/' './' --delete --recursive --safe-links --perms --checksum"
143
+ }
144
+ }
145
+ }
146
+
147
+ return upstreamArtifactImporters
148
+ }
149
+
150
+ def triggeredByUser = (currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) != null)
151
+ def triggerBuild = getTriggerBuild(currentBuild)
152
+ if (!triggerBuild) {
153
+ triggerBuild = [null, null]
154
+ }
155
+ def upstreamBuilds = getUpstreamBuilds(upstreamJobNames, triggerBuild[0], triggerBuild[1])
156
+
157
+ if (!waitForUpstreamBuilds(upstreamBuilds)) {
158
+ return
159
+ }
160
+
@@ -0,0 +1,347 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2014, Gregory Boissinot
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
26
+ >
27
+ <xsl:output method="xml" indent="yes" encoding="utf-8" omit-xml-declaration="no"/>
28
+
29
+ <xsl:param name="apostrophe">&apos;</xsl:param>
30
+ <xsl:template name="processQuote">
31
+ <xsl:param name="string"/>
32
+ <xsl:if test="contains($string, $apostrophe)">
33
+ <xsl:value-of select="substring-before($string, $apostrophe)"/><xsl:text disable-output-escaping="yes">&amp;apos;</xsl:text>
34
+ <xsl:call-template name="processQuote">
35
+ <xsl:with-param name="string">
36
+ <xsl:value-of select="substring-after($string, $apostrophe)"/>
37
+ </xsl:with-param>
38
+ </xsl:call-template>
39
+ </xsl:if>
40
+ <xsl:if test="not(contains($string, $apostrophe))">
41
+ <xsl:value-of select="$string"/>
42
+ </xsl:if>
43
+ </xsl:template>
44
+
45
+ <xsl:template name="testCaseContext">
46
+ <xsl:for-each select="child::*">
47
+ <xsl:text> == [Context] </xsl:text>
48
+ <xsl:value-of select="."/>
49
+ <xsl:text>&#13;</xsl:text>
50
+ </xsl:for-each>
51
+ </xsl:template>
52
+
53
+ <xsl:template match="/TestLog">
54
+
55
+ <xsl:element name="TestSuite">
56
+ <xsl:attribute name="tests">
57
+ <xsl:value-of select="count(//TestCase)"/>
58
+ </xsl:attribute>
59
+
60
+ <xsl:attribute name="errors">
61
+ <xsl:value-of select="count(//TestCase/FatalError)+count(//TestCase/Exception)"/>
62
+ </xsl:attribute>
63
+
64
+ <xsl:attribute name="failures">
65
+ <xsl:value-of select="count(//TestCase/Error)"/>
66
+ </xsl:attribute>
67
+
68
+ <xsl:attribute name="name">MergedTestSuite</xsl:attribute>
69
+
70
+ <xsl:attribute name="skipped">0</xsl:attribute>
71
+
72
+ <xsl:for-each select="//TestCase">
73
+ <xsl:call-template name="testCase"/>
74
+ </xsl:for-each>
75
+ </xsl:element>
76
+
77
+ </xsl:template>
78
+
79
+
80
+ <xsl:template name="testCaseContent">
81
+ <xsl:for-each select="child::*">
82
+ <xsl:variable name="currElt" select="."/>
83
+ <xsl:variable name="currEltName" select="name(.)"/>
84
+ <xsl:choose>
85
+ <xsl:when test="$currEltName='Error'">
86
+ <xsl:text>&#13;</xsl:text>
87
+ <xsl:text>[Error] - </xsl:text>
88
+ <!--<xsl:call-template name="processQuote">-->
89
+ <!--<xsl:with-param name="string">-->
90
+ <!--<xsl:value-of select="$currElt/text()"/>-->
91
+ <!--</xsl:with-param>-->
92
+ <!--</xsl:call-template>-->
93
+ <xsl:value-of select="$currElt"/>
94
+ <xsl:text>&#13;</xsl:text>
95
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
96
+ <xsl:text>&#13;</xsl:text>
97
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
98
+ <xsl:text>&#13;</xsl:text>
99
+ <xsl:for-each select="child::Context">
100
+ <xsl:call-template name="testCaseContext"/>
101
+ </xsl:for-each>
102
+ </xsl:when>
103
+
104
+ <xsl:when test="$currEltName='FatalError'">
105
+ <xsl:text>&#13;</xsl:text>
106
+ <xsl:text>[Exception] - </xsl:text>
107
+ <xsl:call-template name="processQuote">
108
+ <xsl:with-param name="string">
109
+ <xsl:value-of select="$currElt/text()"/>
110
+ </xsl:with-param>
111
+ </xsl:call-template>
112
+ <xsl:text>&#13;</xsl:text>
113
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
114
+ <xsl:text>&#13;</xsl:text>
115
+ <xsl:text> == [Line] -</xsl:text><xsl:value-of select="($currElt)/@line"/>
116
+ <xsl:text>&#13;</xsl:text>
117
+ <xsl:for-each select="child::Context">
118
+ <xsl:call-template name="testCaseContext"/>
119
+ </xsl:for-each>
120
+ </xsl:when>
121
+
122
+ <xsl:when test="$currEltName='Exception'">
123
+ <xsl:text>&#13;</xsl:text>
124
+ <xsl:text>[Exception] - </xsl:text>
125
+ <xsl:call-template name="processQuote">
126
+ <xsl:with-param name="string">
127
+ <xsl:value-of select="$currElt/text()"/>
128
+ </xsl:with-param>
129
+ </xsl:call-template>
130
+ <xsl:choose>
131
+ <xsl:when test="($currElt)/LastCheckpoint">
132
+ <xsl:value-of select="($currElt)/LastCheckpoint/text()"/>
133
+ <xsl:text>&#13;</xsl:text>
134
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/LastCheckpoint/@file"/>
135
+ <xsl:text>&#13;</xsl:text>
136
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/LastCheckpoint/@line"/>
137
+ <xsl:text>&#13;</xsl:text>
138
+ </xsl:when>
139
+
140
+ <xsl:otherwise>
141
+ <xsl:text>&#13;</xsl:text>
142
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
143
+ <xsl:text>&#13;</xsl:text>
144
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
145
+ <xsl:text>&#13;</xsl:text>
146
+ </xsl:otherwise>
147
+ </xsl:choose>
148
+ <xsl:for-each select="child::Context">
149
+ <xsl:call-template name="testCaseContext"/>
150
+ </xsl:for-each>
151
+ </xsl:when>
152
+
153
+ <xsl:when test="$currEltName='Info'"></xsl:when>
154
+
155
+ </xsl:choose>
156
+ </xsl:for-each>
157
+ </xsl:template>
158
+
159
+
160
+ <xsl:template name="testCase">
161
+
162
+ <xsl:variable name="curElt" select="."/>
163
+ <xsl:variable name="suiteName">
164
+ <xsl:for-each select="($curElt/ancestor::TestSuite)">
165
+ <xsl:variable name="nameTrimed" select="replace(./@name,' ','.')"/>
166
+ <xsl:value-of select="$nameTrimed"/><xsl:text>.</xsl:text>
167
+ </xsl:for-each>
168
+ </xsl:variable>
169
+ <xsl:variable name="packageName" select="($suiteName)"/>
170
+
171
+ <xsl:element name="testcase">
172
+ <xsl:variable name="elt" select="(child::*[position()=1])"/>
173
+ <xsl:variable name="time" select="TestingTime"/>
174
+
175
+ <xsl:attribute name="classname">
176
+ <xsl:variable name="packageStr" select="concat($packageName, substring-before(($elt)/@file, '.'))"/>
177
+ <xsl:choose>
178
+ <xsl:when test="ends-with($packageStr, '.')">
179
+ <xsl:variable name="packageStr2"
180
+ select="substring($packageStr, 1, string-length($packageStr)-1)"/>
181
+ <xsl:value-of select="replace($packageStr2,' ','.')"/>
182
+ </xsl:when>
183
+ <xsl:otherwise>
184
+ <xsl:variable name="packageStr2" select="($packageStr)"/>
185
+ <xsl:value-of select="replace($packageStr2,' ','.')"/>
186
+ </xsl:otherwise>
187
+ </xsl:choose>
188
+ </xsl:attribute>
189
+
190
+ <!-- When there is only Exception with LastCheckpoint, override classname attribute -->
191
+
192
+ <xsl:if
193
+ test="((count(child::Exception))=1) and ((count(child::Info)+ count(child::Warning) + count(child::Message))=0)">
194
+ <xsl:variable name="fileName" select="substring-before((./Exception/LastCheckpoint)/@file, '.')"/>
195
+ <xsl:attribute name="classname">
196
+ <!-- <xsl:value-of select="concat($packageName, $fileName)"/> -->
197
+ <xsl:variable name="packageStr" select="concat($packageName, $fileName)"/>
198
+ <xsl:choose>
199
+ <xsl:when test="ends-with($packageStr, '.')">
200
+ <xsl:variable name="packageStr2"
201
+ select="substring($packageStr, 1, string-length($packageStr)-1)"/>
202
+ <xsl:value-of select="replace($packageStr2,' ','.')"/>
203
+ </xsl:when>
204
+ <xsl:otherwise>
205
+ <xsl:variable name="packageStr2" select="($packageStr)"/>
206
+ <xsl:value-of select="replace($packageStr2,' ','.')"/>
207
+ </xsl:otherwise>
208
+ </xsl:choose>
209
+
210
+ </xsl:attribute>
211
+ </xsl:if>
212
+
213
+
214
+ <xsl:attribute name="name">
215
+ <xsl:value-of select="@name"/>
216
+ </xsl:attribute>
217
+
218
+
219
+ <xsl:attribute name="time">
220
+ <xsl:value-of select="$time div 1000000"/>
221
+ </xsl:attribute>
222
+
223
+ <xsl:variable name="nbErrors" select="count(Error)"/>
224
+ <xsl:variable name="nbFatalErrors" select="count(FatalError)+count(Exception)"/>
225
+
226
+ <xsl:choose>
227
+ <xsl:when test="$nbFatalErrors&gt;0">
228
+ <xsl:element name="error">
229
+ <xsl:call-template name="testCaseContent"/>
230
+ </xsl:element>
231
+ </xsl:when>
232
+
233
+ <xsl:when test="$nbErrors&gt;0">
234
+ <xsl:element name="failure">
235
+ <xsl:call-template name="testCaseContent"/>
236
+ </xsl:element>
237
+ </xsl:when>
238
+ </xsl:choose>
239
+
240
+
241
+ <xsl:if test="(count(child::Info)+ count(child::Warning) + count(child::Message))>0">
242
+ <xsl:element name="system-out">
243
+ <xsl:for-each select="child::Info">
244
+ <xsl:variable name="currElt" select="."/>
245
+ <xsl:text>&#13;</xsl:text>
246
+ <xsl:text>[Info] - </xsl:text>
247
+ <xsl:call-template name="processQuote">
248
+ <xsl:with-param name="string">
249
+ <xsl:value-of select="$currElt/text()"/>
250
+ </xsl:with-param>
251
+ </xsl:call-template>
252
+ <xsl:text>&#13;</xsl:text>
253
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
254
+ <xsl:text>&#13;</xsl:text>
255
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
256
+ <xsl:text>&#13;</xsl:text>
257
+ </xsl:for-each>
258
+
259
+ <xsl:for-each select="child::Warning">
260
+ <xsl:variable name="currElt" select="."/>
261
+ <xsl:text>&#13;</xsl:text>
262
+ <xsl:text>[Warning] - </xsl:text>
263
+
264
+ <xsl:call-template name="processQuote">
265
+ <xsl:with-param name="string">
266
+ <xsl:value-of select="$currElt/text()"/>
267
+ </xsl:with-param>
268
+ </xsl:call-template>
269
+ <xsl:text>&#13;</xsl:text>
270
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
271
+ <xsl:text>&#13;</xsl:text>
272
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
273
+ <xsl:text>&#13;</xsl:text>
274
+ </xsl:for-each>
275
+
276
+ <xsl:for-each select="child::Message">
277
+ <xsl:variable name="currElt" select="."/>
278
+ <xsl:text>&#13;</xsl:text>
279
+ <xsl:text>[Message] - </xsl:text>
280
+ <xsl:call-template name="processQuote">
281
+ <xsl:with-param name="string">
282
+ <xsl:value-of select="$currElt/text()"/>
283
+ </xsl:with-param>
284
+ </xsl:call-template>
285
+ <xsl:text>&#13;</xsl:text>
286
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
287
+ <xsl:text>&#13;</xsl:text>
288
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
289
+ <xsl:text>&#13;</xsl:text>
290
+ </xsl:for-each>
291
+
292
+ <xsl:for-each select="child::*/Context">
293
+ <xsl:call-template name="testCaseContext"/>
294
+ </xsl:for-each>
295
+
296
+ </xsl:element>
297
+ </xsl:if>
298
+
299
+
300
+ <xsl:if test="count(child::Exception)>0">
301
+ <xsl:element name="system-err">
302
+ <xsl:for-each select="child::Exception">
303
+ <xsl:variable name="currElt" select="."/>
304
+ <xsl:text>&#13;</xsl:text>
305
+ <xsl:text>[Exception] - </xsl:text>
306
+ <xsl:call-template name="processQuote">
307
+ <xsl:with-param name="string">
308
+ <xsl:value-of select="$currElt/text()"/>
309
+ </xsl:with-param>
310
+ </xsl:call-template>
311
+ <xsl:choose>
312
+ <xsl:when test="($currElt)/LastCheckpoint">
313
+ <xsl:value-of select="($currElt)/LastCheckpoint/text()"/>
314
+ <xsl:text>&#13;</xsl:text>
315
+ <xsl:text> == [File] - </xsl:text><xsl:value-of
316
+ select="($currElt)/LastCheckpoint/@file"/>
317
+ <xsl:text>&#13;</xsl:text>
318
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of
319
+ select="($currElt)/LastCheckpoint/@line"/>
320
+ <xsl:text>&#13;</xsl:text>
321
+ </xsl:when>
322
+
323
+ <xsl:otherwise>
324
+ <xsl:text>&#13;</xsl:text>
325
+ <xsl:text> == [File] - </xsl:text><xsl:value-of select="($currElt)/@file"/>
326
+ <xsl:text>&#13;</xsl:text>
327
+ <xsl:text> == [Line] - </xsl:text><xsl:value-of select="($currElt)/@line"/>
328
+ <xsl:text>&#13;</xsl:text>
329
+ </xsl:otherwise>
330
+ </xsl:choose>
331
+
332
+ </xsl:for-each>
333
+
334
+ <xsl:for-each select="child::*/Context">
335
+ <xsl:call-template name="testCaseContext"/>
336
+ </xsl:for-each>
337
+
338
+ </xsl:element>
339
+ </xsl:if>
340
+
341
+
342
+ </xsl:element>
343
+
344
+ </xsl:template>
345
+
346
+ <xsl:template match="text()|@*"/>
347
+ </xsl:stylesheet>