Strange behavior....

View: New views
10 Messages — Rating Filter:   Alert me  

Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

Hello,
I have the following class:

class XmlDiffOracle(e: Option[File], a: Option[File])
extends FileDiffOracle(e,a) {
 
  var explainFailure : List[Difference] = Nil
 
  var explainError : String = ""

  override  def compareFiles(exp : File, act: File) : TestOutcome = {
    val diff = new DetailedDiff(new org.custommonkey.xmlunit.Diff(new InputSource(new FileInputStream(exp)),
                                                                                        new InputSource(new FileInputStream(act))))
    if(diff.identical)
      TestSuccessful
    else {
      explainFailure = new BufferWrapper[Difference]{
      def underlying = diff.getAllDifferences                                                                                                                                          
      }.toList
      println(explainFailure)
      TestFailure
    }
  }
}

and the following test case :

  def testFailedXmlOracleStoresDifferenceList = {
    val oracle = new XmlDiffOracle(Some(xexpected),Some(xactual))
    // should compare the found diffrences
    assertEquals("expected failure", TestFailure, oracle.outcome)
    println(oracle)
    assertEquals(4,oracle.explainFailure.length)
  }

This test fails (ie. oracle.explainFailure.length is 0). However, the
println(x) in the class displays the rigght content, whereas the
second one displays an empty list. Is there something I missed about
var x ... behavior or java wrapping ?

Regards,
--
OQube < software engineering \ génie logiciel >
Arnaud Bailly, Dr.
\web> http://www.oqube.com


Re: Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

Anyone ? Am I really dumb ?
--
OQube < software engineering \ génie logiciel >
Arnaud Bailly, Dr.
\web> http://www.oqube.com


Re: Strange behavior....

by Eric Torreborre :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

Don't know,...

Where is the code of the "outcome" method? I guess it must call "compareFiles", but what else does it do?

E.

Arnaud Bailly wrote:
Hello,
I have the following class:

class XmlDiffOracle(e: Option[File], a: Option[File])
extends FileDiffOracle(e,a) {
 
  var explainFailure : List[Difference] = Nil
 
  var explainError : String = ""

  override  def compareFiles(exp : File, act: File) : TestOutcome = {
    val diff = new DetailedDiff(new org.custommonkey.xmlunit.Diff(new InputSource(new FileInputStream(exp)),
                                                                                        new InputSource(new FileInputStream(act))))
    if(diff.identical)
      TestSuccessful
    else {
      explainFailure = new BufferWrapper[Difference]{
      def underlying = diff.getAllDifferences                                                                                                                                          
      }.toList
      println(explainFailure)
      TestFailure
    }
  }
}

and the following test case :

  def testFailedXmlOracleStoresDifferenceList = {
    val oracle = new XmlDiffOracle(Some(xexpected),Some(xactual))
    // should compare the found diffrences
    assertEquals("expected failure", TestFailure, oracle.outcome)
    println(oracle)
    assertEquals(4,oracle.explainFailure.length)
  }

This test fails (ie. oracle.explainFailure.length is 0). However, the
println(x) in the class displays the rigght content, whereas the
second one displays an empty list. Is there something I missed about
var x ... behavior or java wrapping ?

Regards,
--
OQube < software engineering \ génie logiciel >
Arnaud Bailly, Dr.
\web> http://www.oqube.com

Re: Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

Eric Torreborre <etorreborre@...> writes:

> Don't know,...
>
> Where is the code of the "outcome" method? I guess it must call
> "compareFiles", but what else does it do?
>

Nothing else. just extracting the files from an option classe. Here is the full code:


/**
 * A trait for understanding the outcome of a test.
 */
trait TestOracle {
   
  val outcome : TestOutcome
}


/**
 * This class produces a TestOutcome for a single test.
 */
abstract class FileDiffOracle(val expected: Option[File], val actual: Option[File]) extends TestOracle{

  /**
   * This oracle compares two text files and produces an oracle according
   * to the following rules:
   * <ul>
   *  <li>If the two input streams contain identical text, the test is a TestSuccess</li>
   *  <li>If the two input streams contain different text, the test is a TestFailure</li>
   *  <li>If the first inputstream is empty, the test is a TestSkipped</li>
   *  <li>If second imput stream is empty, the test is a TestError</li>
   * </ul>
   * @return a TestOutcome
   */
  override val outcome : TestOutcome = {
    (expected,actual) match {
      case (None,_)            => TestSkipped
      case (_,None)            => TestError
      case (Some(e),Some(a))   => compareFiles(e,a)
    }
  }        

  /**
   * Method that compute the differences between files.
   * To implement by subclasses.
   */
  def compareFiles(exp : File, act: File) : TestOutcome;

}

/**
 * Textual diff comparison oracle.
 * The outcome is produced by comparing the two input streams given
 * as actual and expected string. The oracle produces an outcome based
 * on the <em>textual</em> differences between the two files.<br />
 * <strong>Note</strong>This class transforms the content of the files into
 * a String which is not optimal and may cause large memory consumption. It
 * would be better to parse the files on the fly but this implies adapting the
 * algorithm used in DiffMatchPatch.
 */
class TextDiffOracle(e: Option[File], a: Option[File])(implicit val lines : Boolean)
extends FileDiffOracle(e,a) {
 
  var explainFailure : List[Diff] = Nil
 
  var explainError : String = ""
 
  override   def compareFiles(exp : File, act: File) : TestOutcome = {
    val diff = new DiffMatchPatch()
    val stre = makeString(exp)
    val stra = makeString(act)
    val diffs  = diff.diff_main(stre,stra,lines)
    if(diffs.size == 1 && diffs.get(0).asInstanceOf[Diff].operation == DiffMatchPatch.Operation.EQUAL)
      TestSuccessful
    else {
      this.explainFailure = diffs.toArray.map(_.asInstanceOf[Diff]).toList.filter(dif => dif.operation != DiffMatchPatch.Operation.EQUAL)
      println(explainFailure.length)
      TestFailure
    }
  }        

  /**
   * Construct  a string from the content of a file (if it exists) using
   * default locale.
   */
   def makeString(f : File) : String =  {  
     val bos = new ByteArrayOutputStream
     val fis = new FileInputStream(f)
     new Pipe(bos,fis).pump
     bos.toString
   }
}

/**
 * XML diff comparison oracle.
 * The outcome is produced by comparing two files as XML content.
 */
class XmlDiffOracle(e: Option[File], a: Option[File])
extends FileDiffOracle(e,a) {
 
  var explainFailure : List[Difference] = Nil
 
  var explainError : String = ""

  override  def compareFiles(exp : File, act: File) : TestOutcome = {
    val diff = new DetailedDiff(new org.custommonkey.xmlunit.Diff(new InputSource(new FileInputStream(exp)),
                                                                  new InputSource(new FileInputStream(act))))
    if(diff.identical)
      TestSuccessful
    else {
      explainFailure = new BufferWrapper[Difference]{
        def underlying = diff.getAllDifferences                                                                                                                                          
      }.toList
      println(explainFailure)
      TestFailure
    }
  }

  override def toString : String = explainFailure.toString
}


--
OQube < software engineering \ génie logiciel >
Arnaud Bailly, Dr.
\web> http://www.oqube.com


Re: Strange behavior....

by Eric Torreborre :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

I tried with a simplified version of the code using a java.util.ArrayList as an underlying to the BufferWrapper and it works.

So I suspect this is may be something with the collection you're using as an underlying to the BufferWrapper. Can you try just using a simple ArrayList to see what happens?

Eric.

Re: Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

Hello Eric,
Thanks for your interest :)
I checked in the source code of xmlunit which is the library I am using, and the getAllDifferences method
returns an ArrayList.

There must be something obvious I am missing :(

Regards
--
OQube < software engineering \ génie logiciel >
Arnaud Bailly, Dr.
\web> http://www.oqube.com


Re: Strange behavior....

by Eric Torreborre :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message


Re: Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message


????!!!!!

No, I do not have static things (static is evil :)). I will try
something else tomorrow. Thanks for very much for taking the time
investigating my problem, that's really puzzling. I am pretty sure
this is some silly thing so obvious I am skipping it.

Best regards,
--
Arnaud Bailly, PhD
OQube - Software Engineering
http://www.oqube.com


Re: Strange behavior....

by Eric Torreborre :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message

> ????!!!!!
> No, I do not have static things (static is evil :))

I was just thinking "hidden state". But I like working with people infuriated by the mere mentioning of static variables! Keep me updated, I'm curious.

Eric.

Re: Strange behavior....

by Arnaud Bailly :: Rate this Message:

Reply (Restricted by the Administrator) | Reply to Author | View Threaded | Show Only this Message


Hello (and Happy New Year),
I found the reason for my problem. Here is a skeleton code
illustrating the issues, the reason should be obvious from this code ;-):

trait Oracle {
      val outcome : Outcome;
}

abstract class AbstractOracle(val f: Option[File]) extends Oracle {

      override val outcome : Outcome = {
          f match {
            case None    => Error
            case Some(f) => test(f)   // (1)
          }
      }      

      def test(f : File) : Outcome;
}

class ConcreteOracle(f1 : Option[File]) extends AbstractOracle(f1) {

  var explainFailure : List[Diff] = Nil   // (2)

  override def test(f : File) : Outcome = {
    if(testok(f))
       Success
    else {
      explainFailure = explain(f)   // (3)
      Failure
    }
  }
}

The problem is that (2) gets called after (1) because the constructor
code of ConcreteOracle is called after its superclasses'. Hence
explainFailure is correctly computed in (3) and reset to Nil in (2).
Some classical trap with OO constructor dispatch...

Everything works fine when I write :

abstract class AbstractOracle(val f: Option[File]) extends Oracle {

      override lazy val outcome : Outcome = {

Then outcome is called only when requested, which does occur after the
constructor sequence has finished.

BTW, lazy values does not seem to be memoized (according to the
generated bytecode at least) or maybe I missed something. Is there a
deep reason for this ? I think that Haskell's (and maybe OCaml's) lazy
values are memoized which leads to better performances.

Best regards,

--
Arnaud Bailly, PhD
OQube - Software Engineering
http://www.oqube.com