TestResultStX.st
author Claus Gittinger <cg@exept.de>
Wed, 29 May 2019 01:12:49 +0200
changeset 747 1dcb53cf964d
parent 744 7f03f8bc1a15
child 761 757bd726452d
permissions -rw-r--r--
#FEATURE by cg class: TestCase added: #invokeTestMethod changed: #performTest support timeout annotation

"{ Package: 'stx:goodies/sunit' }"

"{ NameSpace: Smalltalk }"

TestResult subclass:#TestResultStX
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'SUnit-Smalltalk/X'
!


!TestResultStX class methodsFor:'utilities'!

sourceFilenameOfClass:aClass
    "that is ST/X specific"
    "no Smalltalk/X dialect detection needed..."

    |sourceStream testClassSourceFile|

    sourceStream := aClass sourceStream.
    sourceStream notNil ifTrue:[
        [
            sourceStream := sourceStream stream.
            sourceStream isFileStream ifTrue:[
                testClassSourceFile := sourceStream pathName asFilename pathName.
            ].
        ] ensure:[
            sourceStream notNil ifTrue:[ sourceStream close ].
        ]
    ].
    ^ testClassSourceFile
! !

!TestResultStX methodsFor:'accessing'!

endTime
    ^ endTime
!

endTime:aTimestamp
    "sets the overall (suite) end time"

    endTime := aTimestamp.
!

executionTime
    "return the execution time (in seconds). If asked before or during a run, return nil"

    |t1 t2|

    (t1 := self startTime) isNil ifTrue:[^ nil].
    (t2 := self endTime) isNil ifTrue:[^ nil].
    ^ t2 secondDeltaFrom: t1

    "
     |a b|

     a := Timestamp now.
     Delay waitForMilliseconds:567.
     b := Timestamp now.
     b - a   
    "
!

startTime
    ^ timestamp
!

startTime:aTimestamp
    "sets the overall (suite) start time"

    timestamp := aTimestamp.
! !

!TestResultStX methodsFor:'outcome'!

printLineForContextForJavaCompatibleStackTrace:con on:aStream
    "why in java-backtrace format?
     Because then jenkins will be able to extract sourcefile and linenumber
     and generate links in the report page.
     I am not willing to write another plugin for this - using junit compatible format"

    |cls classSourceStream method s lineNumberOfMethod lineNumberInFile relPath methodSourcePosition|

    "/ used to be:
    "/ con printOn:aStream.

    "/ be careful: some tests generate methods on the fly, which are unbound!!
    "/ or even (javascript/other languages) may not have a method at all!!!!

    con fixAllLineNumbers.
    (method := con method) notNil ifTrue:[
        cls := method mclass.
        cls notNil ifTrue:[
            relPath := cls package copyReplaceAll:$: with:$/.
            relPath := relPath asUnixFilenameString.
            relPath := (relPath asFilename construct:cls theNonMetaclass classFilename) name.

            method isJavaMethod ifTrue:[
                lineNumberOfMethod :=  method lineNumberForPC0: 0.
            ] ifFalse:[
                methodSourcePosition := method sourcePosition.
                methodSourcePosition notNil ifTrue:[
                    classSourceStream := cls sourceStream.
                    classSourceStream notNil ifTrue:[
                        "/ sigh - we have the lineNumber within the method,
                        "/ and the characterPosition of the method's start.
                        "/ need to calculate the absolute lineNumber in the file
                        s := LineNumberReadStream on:classSourceStream "(classSourceFile asFilename readStream)".
                        [
                            [s atEnd not and:[s position < methodSourcePosition]] whileTrue:[
                                s nextLine
                            ].
                            lineNumberOfMethod := s lineNumber.
                        ] ensure:[
                            s close.
                        ].
                    ].
                ]
            ]
        ].
    ].

    "/ output something (will not generate a ref to the source file)
    con printReceiverOn:aStream.
    aStream nextPutAll:' >> '.
    con selector printOn:aStream.
    aStream nextPutAll:' ['.
    con lineNumber printOn:aStream.
    aStream nextPut:$].

    relPath notNil ifTrue:[
        lineNumberOfMethod notNil ifTrue:[
            lineNumberInFile := lineNumberOfMethod + con lineNumber - 1.
            aStream nextPutAll:(' (%1:%2)'
                                bindWith: relPath
                                with: lineNumberInFile)
        ] ifFalse:[
            aStream nextPutAll:(' (%1)'
                                bindWith: relPath)
        ].
    ].
    aStream cr.

    "Modified: / 05-08-2012 / 12:00:00 / cg"
    "Modified: / 12-11-2013 / 23:09:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

rememberEndTime
    "remembers the endTime of the current test (in outcome)"

    ^outcome endTime: Timestamp now

    "Created: / 16-08-2011 / 17:36:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

rememberException:ex
    "common for failure and error:
     called when a test fails. testCase is the failed
     testcase, ex is platform specific object describing
     the failure. Actually, on all platforms except GemStone,
     ex is an instance of an exception that caused the failure"

    |backtrace|

    ((Smalltalk respondsTo:#isSmalltalkX) and:[Smalltalk isSmalltalkX]) ifTrue:[
        "remember the backtrace (as string, to prevent objects from being kept forwever
         from being garbage collected, the signal (i.e. the exception class) and the
         exceptions message (description).
         Would like to have an exceptionInfo obejct for that, but that might be hard to
         get returned back into the main stream sunit package..."

        backtrace := String streamContents:[:s |
                        |con topReached|

                        "could use printAllOn:s, but noone is interested in contexts above the
                         testcase's runtest context"
                        topReached := false.
                        con := ex suspendedContext.
                        [
                            con notNil and:[topReached not]
                        ] whileTrue:[
                            self printLineForContextForJavaCompatibleStackTrace:con on:s.
                            topReached := ((con selector == outcome selector) or:[con selector == #runCase])
                                          and:[con receiver == outcome
                                               or:[ con receiver == outcome testCase ]].
                            con := con sender.
                        ]
                     ].

        outcome exceptionDetail:(Dictionary new
                                    at:#exception put:ex creator;
                                    at:#description put:ex description;
                                    at:#backtrace put:backtrace;
                                    yourself).
        ^ self.
    ].

    "add other dialect specifics here"

    "Created: / 06-08-2011 / 11:29:23 / cg"
    "Created: / 16-08-2011 / 17:32:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (format): / 22-05-2017 / 18:26:41 / mawalch"
!

rememberOutput: aString
    outcome collectedOutput: aString

    "Created: / 16-08-2011 / 18:18:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

rememberStartTime
    "remembers the startTime of the current test (in outcome)"

    ^outcome startTime: Timestamp now

    "Created: / 16-08-2011 / 17:36:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!TestResultStX methodsFor:'running'!

performCase: aTestCase
    "while performing a testcase, any output to the Transcript is also
     collected and is attached to the test result. Nice when tests are executed
     automatically, for example by jenkins.
     Q: I think, we MUST lock this, 
        or better: acquire the lock and making collecting explicit via
                   call to 'self collectingTranscriptOutputDo:[...]' in the testcase.
        Otherwise we'll run into trouble, when executing multiple tests in parallel.
"

    |savedStdout savedTranscript collector|

    savedStdout := Stdout.
    savedTranscript := Transcript.
    collector := CharacterWriteStream new:100.
    [
        Stdout := SplittingWriteStream on:collector and: Stdout.
        Transcript := SplittingWriteStream on:collector and: Transcript.
        super performCase: aTestCase.
    ] ensure:[
        Stdout := savedStdout.
        Transcript := savedTranscript.
        self rememberOutput: collector contents.
    ].

    "Created: / 16-08-2011 / 18:18:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 29-08-2013 / 11:15:36 / cg"
! !

!TestResultStX class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !