Fantom

 

//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   13 Jan 09  Brian Frank  Creation
//

using xml

**
** XmlTest - test XML encoding/decoding
**
class XmlTest : ObixTest
{

//////////////////////////////////////////////////////////////////////////
// ObjTree
//////////////////////////////////////////////////////////////////////////

  Void testObjTree()
  {
    // simple
    verifyParse(
      "<?xml version='1.0?>
       <obj href='http://foo/obix/'/>",
       ObixObj { href = `http://foo/obix/` })

    // with children and PI
    verifyParse(
      "<?xml version='1.0?>
       <obj href='http://foo/obix/'>
         <obj name='a'>
           <obj name='ax'/>
         </obj>
         <?pi?>
         <obj name='b'>
           <!-- comment -->
           <obj name='bx'/>
           <obj name='by'>
             <obj name='byi'/>
           </obj>
           <?pi?>
           <obj name='bz'><?pi?></obj>
         </obj>
       </obj>
       ",
       ObixObj
       {
         href = `http://foo/obix/`
         ObixObj
         {
           name = "a"
           ObixObj { name = "ax" },
         },
         ObixObj
         {
           name = "b"
           ObixObj { name = "bx" },
           ObixObj { name = "by"; ObixObj { name="byi"}, },
           ObixObj { name = "bz" },
         }
       })
  }

//////////////////////////////////////////////////////////////////////////
// Vals
//////////////////////////////////////////////////////////////////////////

  Void testBool()
  {
    verifyParse(
      "<obj>
        <bool name='def'/>
        <bool name='a' val='true'/>
        <bool name='b' val='false'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val=false },
         ObixObj { name="a"; val=true },
         ObixObj { name="b"; val=false },
       })
   }

  Void testInt()
  {
    verifyParse(
      "<obj>
        <int name='def'/>
        <int name='a' val='3'/>
        <int name='b' val='-1234'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val=0 },
         ObixObj { name="a"; val=3 },
         ObixObj { name="b"; val=-1234},
       })
   }

  Void testReal()
  {
    verifyParse(
      "<obj>
        <real name='def'/>
        <real name='a' val='2'/>
        <real name='b' val='-2.4'/>
        <real name='c' val='4e10'/>
        <real name='nan' val='NaN'/>
        <real name='posInf' val='INF'/>
        <real name='negInf' val='-INF'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val=0f },
         ObixObj { name="a"; val=2f },
         ObixObj { name="b"; val=-2.4f },
         ObixObj { name="c"; val=4e10f },
         ObixObj { name="nan"; val=Float.nan },
         ObixObj { name="posInf"; val=Float.posInf },
         ObixObj { name="negInf"; val=Float.negInf },
       })

     verifyEq(ObixObj { val = Float.nan }.valToStr, "NaN")
     verifyEq(ObixObj { val = Float.posInf }.valToStr, "INF")
     verifyEq(ObixObj { val = Float.negInf }.valToStr, "-INF")
   }

  Void testStr()
  {
    verifyParse(
      "<obj>
        <str name='def'/>
        <str name='a' val='hi'/>
        <str name='b' val='&gt; &apos; &amp; &quot; &lt;'/>
        <str name='c' val='32\u00B0\nline2'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val="" },
         ObixObj { name="a"; val="hi" },
         ObixObj { name="b"; val="> ' & \" <" },
         ObixObj { name="c"; val="32\u00B0\nline2" },
       })
   }

  Void testUri()
  {
    verifyParse(
      "<obj>
        <uri name='def'/>
        <uri name='a' val='http://foo/'/>
        <uri name='b' val='http://foo/path%20name?foo=bar+baz'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val=`` },
         ObixObj { name="a"; val=`http://foo/` },
         ObixObj { name="b"; val=`http://foo/path name?foo=bar baz` },
       })
   }

  Void testEnum()
  {
    verifyParse(
      "<obj>
        <enum name='def'/>
        <enum name='a' val='slow'/>
       </obj>",
       ObixObj
       {
         ObixObj { elemName="enum"; name="def"; isNull=true },
         ObixObj { elemName="enum"; name="a"; val="slow" },
       })
   }

  Void testAbstime()
  {
    verifyParse(
      "<obj>
        <abstime name='def'/>
        <abstime name='a' val='2009-01-15T13:54:00Z'/>
        <abstime name='b' val='2009-01-15T13:54:00-05:00'/>
        <abstime name='c' val='2009-01-15T13:54:00Z' tz='London'/>
        <abstime name='d' val='2009-01-15T13:54:00-05:00' tz='America/New_York'/>
        <abstime name='e' val='9999-12-31T23:59:59.999Z'/>
        <abstime name='f' val='0000-01-01T00:00:00Z'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; elemName="abstime"; isNull=true },
         ObixObj { name="a"; val=DateTime(2009, Month.jan, 15, 13, 54, 0, 0, TimeZone.utc) },
         ObixObj { name="b"; val=DateTime(2009, Month.jan, 15, 13, 54, 0, 0, TimeZone("Etc/GMT+5")) },
         ObixObj { name="c"; val=DateTime(2009, Month.jan, 15, 13, 54, 0, 0, TimeZone("London")) },
         ObixObj { name="d"; val=DateTime(2009, Month.jan, 15, 13, 54, 0, 0, TimeZone("New_York")); tz=TimeZone("New_York") },
         ObixObj { name="e"; val=DateTime(2099, Month.dec, 31, 23, 59, 59, 999_000_000, TimeZone.utc) },
         ObixObj { name="f"; val=DateTime(1901, Month.jan,  1,  0,  0,  0, 0, TimeZone.utc) },
       })
  }

  Void testReltime()
  {
    verifyParse(
      "<obj>
        <reltime name='def'/>
        <reltime name='a' val='PT45S'/>
        <reltime name='b' val='PT0.1S'/>
        <reltime name='c' val='P2DT20H15M'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="def"; val=0sec },
         ObixObj { name="a";   val=45sec },
         ObixObj { name="b";   val=100ms },
         ObixObj { name="c";   val=2day+20hr+15min },
       })
  }

  Void testDate()
  {
    verifyParse(
      "<obj>
        <date name='def'/>
        <date name='a' val='2010-01-30'/>
        <date name='b' val='1995-12-05' tz='America/Chicago'/>
        <date name='c' val='9999-12-31'/>
       </obj>",
       ObixObj
       {
         ObixObj { elemName="date"; name="def"; isNull=true },
         ObixObj { name="a"; val=Date(2010, Month.jan, 30) },
         ObixObj { name="b"; val=Date(1995, Month.dec, 05); tz=TimeZone("Chicago") },
         ObixObj { name="c"; val=Date(9999, Month.dec, 31) },
       })
   }

  Void testTime()
  {
    verifyParse(
      "<obj>
        <time name='def'/>
        <time name='a' val='05:30:20'/>
        <time name='b' val='23:00:00.456' tz='Europe/London'/>
       </obj>",
       ObixObj
       {
         ObixObj { elemName="time"; name="def"; isNull=true },
         ObixObj { name="a"; val=Time(5, 30, 20) },
         ObixObj { name="b"; val=Time(23, 0, 0, 456ms.ticks); tz=TimeZone("London") },
       })
   }

  Void testValErrs()
  {
    // turn obj's with val into str
    verifyParse("<obj val='foo'/>", ObixObj { elemName="str"; val="foo" })

    verifyParseErr("<obj><op val='bad'/></obj>")
  }

//////////////////////////////////////////////////////////////////////////
// Facets
//////////////////////////////////////////////////////////////////////////

  Void testDisplay()
  {
    verifyParse(
      "<obj>
        <obj name='a' displayName='Alpha' />
        <obj name='b' display='The Beta'/>
        <int name='c' displayName='Gamma' display='The Gamma' val='5'/>
        <obj name='d' displayName='&apos;\"&lt;&gt;' />
        <obj name='e' display='&apos;\"&lt;&gt;' />
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; displayName="Alpha" },
         ObixObj { name="b"; display="The Beta" },
         ObixObj { name="c"; displayName="Gamma"; display="The Gamma"; val=5 },
         ObixObj { name="d"; displayName="'\"<>" },
         ObixObj { name="e"; display="'\"<>" },
       })
   }

  Void testIcon()
  {
    verifyParse(
      "<obj>
        <obj name='a' icon='http://foo/icons/a.png' />
        <obj name='b' icon=\"http://foo/icon%20dir/?foo=bar+bar\" />
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; icon=`http://foo/icons/a.png` },
         ObixObj { name="b"; icon=`http://foo/icon dir/?foo=bar bar` },
       })
   }

  Void testMinMax()
  {
    verifyParse(
      "<obj>
        <int name='a' min='0'/>
        <int name='b' max='100'/>
        <real name='c' min='1' max='99' />
        <real name='d' min='-INF' max='INF'/>
        <str name='e' min='2' max='20'/>
        <abstime name='f' isNull='true' min='2000-01-01T00:00:00Z' max='2000-12-31T23:59:59Z'/>
        <reltime name='g' min='PT3S' max='PT1M'/>
        <date name='h' isNull='true' min='2000-01-01' max='2000-12-31'/>
        <time name='i' isNull='true' min='01:00:00' max='12:00:00'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; val=0; min=0 },
         ObixObj { name="b"; val=0; max=100 },
         ObixObj { name="c"; val=0f; min=1f; max=99f },
         ObixObj { name="d"; val=0f; min=Float.negInf; max=Float.posInf },
         ObixObj { name="e"; val=""; min=2; max=20 },
         ObixObj { name="f"; elemName="abstime"; isNull=true;
                   min=DateTime(2000, Month.jan, 1, 0, 0, 0, 0, TimeZone.utc)
                   max=DateTime(2000, Month.dec, 31, 23, 59, 59, 0, TimeZone.utc) },
         ObixObj { name="g"; val=0sec; min=3sec; max=1min },
         ObixObj { name="h"; elemName="date"; isNull=true
                   min=Date(2000, Month.jan, 1); max=Date(2000, Month.dec, 31) },
         ObixObj { name="i"; elemName="time"; isNull=true
                   min=Time(1, 0, 0); max=Time(12, 0, 0) },
       })
   }

  Void testPrecision()
  {
    verifyParse(
      "<obj>
        <real name='a' val='75.00' precision='2' />
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; val=75f; precision=2 },
       })
   }

  Void testRange()
  {
    verifyParse(
      "<obj>
        <enum name='a' val='1' range='http://foo/range' />
        <enum name='b' val='2' range=\"http://foo/range%20val/\" />
       </obj>",
       ObixObj
       {
         ObixObj { elemName="enum"; name="a"; val="1"; range=`http://foo/range` },
         ObixObj { elemName="enum"; name="b"; val="2"; range=`http://foo/range val/` },
       })
   }

  Void testStatus()
  {
    verifyParse(
      "<obj>
        <obj name='a' />
        <obj name='b' status='disabled'/>
        <obj name='c' status='fault'/>
        <obj name='d' status='down'/>
        <obj name='e' status='unackedAlarm'/>
        <obj name='f' status='alarm'/>
        <obj name='g' status='unacked'/>
        <obj name='h' status='overridden'/>
        <obj name='i' status='ok'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; status=Status.ok },
         ObixObj { name="b"; status=Status.disabled },
         ObixObj { name="c"; status=Status.fault },
         ObixObj { name="d"; status=Status.down },
         ObixObj { name="e"; status=Status.unackedAlarm },
         ObixObj { name="f"; status=Status.alarm },
         ObixObj { name="g"; status=Status.unacked },
         ObixObj { name="h"; status=Status.overridden },
         ObixObj { name="i"; status=Status.ok },
       })
   }

  Void testUnit()
  {
    verifyParse(
      "<obj>
        <int name='a' unit='obix:units/meter' />
        <int name='b' unit='obix:units/fahrenheit' />
        <int name='c' unit='obix:units/unknown_unit' />
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; val=0; unit=Unit("meter") },
         ObixObj { name="b"; val=0; unit=Unit("fahrenheit") },
         ObixObj { name="c"; val=0 },
       })
   }

  Void testWritable()
  {
    verifyParse(
      "<obj>
        <int name='a' />
        <int name='b' writable='true'/>
        <int name='c' writable='false'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; val=0; writable=false },
         ObixObj { name="b"; val=0; writable=true },
         ObixObj { name="c"; val=0; writable=false },
       })
   }

//////////////////////////////////////////////////////////////////////////
// Contracts
//////////////////////////////////////////////////////////////////////////

  Void testIs()
  {
    verifyParse(
      "<obj>
        <obj name='a' is='obix:Point'/>
        <obj name='b' is='obix:Point obix:WritablePoint'/>
        <obj name='c' is='http://foo/a%20b'/>
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; contract=Contract([`obix:Point`]) },
         ObixObj { name="b"; contract=Contract([`obix:Point`, `obix:WritablePoint`]) },
         ObixObj { name="c"; contract=Contract([`http://foo/a b`]) },
       })
  }

//////////////////////////////////////////////////////////////////////////
// List
//////////////////////////////////////////////////////////////////////////

  Void testList()
  {
    verifyParse(
      "<list of='obix:Point'>
         <real val='1'/>
         <real val='2'/>
       </list>",
       ObixObj
       {
         elemName = "list"
         of = Contract([`obix:Point`])
         ObixObj { val=1f },
         ObixObj { val=2f },
       })
  }

//////////////////////////////////////////////////////////////////////////
// Op
//////////////////////////////////////////////////////////////////////////

  Void testOp()
  {
    verifyParse(
      "<obj>
         <op name='a' in='/in'/>
         <op name='b' out='/out1 /out2'/>
         <op name='c' in='/in' out='/out' />
       </obj>",
       ObixObj
       {
         ObixObj { elemName="op"; name="a"; in=Contract([`/in`]) },
         ObixObj { elemName="op"; name="b"; out=Contract([`/out1`, `/out2`]) },
         ObixObj { elemName="op"; name="c"; in=Contract([`/in`]); out=Contract([`/out`]) },
       })
  }

//////////////////////////////////////////////////////////////////////////
// Feed
//////////////////////////////////////////////////////////////////////////

  Void testFeed()
  {
    verifyParse(
      "<obj>
         <feed name='a' of='/in'/>
         <feed name='b' out='/out1 /out2'/>
         <feed name='c' of='/in' out='/out' />
       </obj>",
       ObixObj
       {
         ObixObj { elemName="feed"; name="a"; of=Contract([`/in`]) },
         ObixObj { elemName="feed"; name="b"; out=Contract([`/out1`, `/out2`]) },
         ObixObj { elemName="feed"; name="c"; of=Contract([`/in`]); out=Contract([`/out`]) },
       })
  }

//////////////////////////////////////////////////////////////////////////
// Unknown Elements
//////////////////////////////////////////////////////////////////////////

  Void testUnknownElems()
  {
    verifyParse(
      "<obj>
         <newOne/>
         <int name='a' val='100' custom='foo'><custom/></int>
         <foo>
           <bar/>
           <obj name='foo'/>
         </foo>
         <obj name='b'>
           <real name='c' val='0'/>
         </obj>
       </obj>",
       ObixObj
       {
         ObixObj { name="a"; val=100 },
         ObixObj { name="b"; ObixObj { name="c"; val=0f }, },
       })
  }

//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////

  Void verifyParse(Str s, ObixObj expected)
  {
    // parse and compare actual result
    actual := ObixXmlParser(s.in).parse
   verifyObj(actual, expected)

    // write to string and roundtrip
    buf := Buf()
    actual.writeXml(buf.out)
    rt := ObixObj.readXml(buf.flip.readAllStr.in)
    verifyObj(rt, expected)
  }

  Void verifyParseErr(Str s)
  {
    verifyErr(XErr#) { ObixObj.readXml(s.in) }
  }

}