dbus-0.10.14: tests/DBusTests/Introspection.hs
-- Copyright (C) 2010-2012 John Millikin <john@john-millikin.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
module DBusTests.Introspection (test_Introspection) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (liftM, liftM2)
import Test.QuickCheck
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import DBus
import qualified DBus.Introspection as Introspection
import DBusTests.InterfaceName ()
import DBusTests.MemberName ()
import DBusTests.ObjectPath ()
import DBusTests.Signature ()
import DBusTests.Util (halfSized)
test_Introspection :: TestTree
test_Introspection = testGroup "Introspection"
[ test_XmlPassthrough
, test_XmlParse
, test_XmlParseFailed
, test_XmlWriteFailed
]
test_XmlPassthrough :: TestTree
test_XmlPassthrough = testProperty "xml-passthrough" $ \obj -> let
path = Introspection.objectPath obj
Just xml = Introspection.formatXML obj
in Introspection.parseXML path xml == Just obj
test_XmlParse :: TestTree
test_XmlParse = testCase "xml-parse" $ do
-- root object path can be inferred
Introspection.parseXML (objectPath_ "/") "<node><node name='foo'/></node>"
@?= Just (Introspection.object (objectPath_ "/"))
{ Introspection.objectChildren =
[ Introspection.object (objectPath_ "/foo")
]
}
test_XmlParseFailed :: TestTree
test_XmlParseFailed = testCase "xml-parse-failed" $ do
Nothing @=? Introspection.parseXML (objectPath_ "/") "<invalid>"
Nothing @=? Introspection.parseXML (objectPath_ "/") "<invalid/>"
-- invalid property access
Nothing @=? Introspection.parseXML (objectPath_ "/")
"<node>\
\ <interface name='com.example.Foo'>\
\ <property type='s' access='invalid'>\
\ </property>\
\ </interface>\
\</node>"
-- invalid parameter type
Nothing @=? Introspection.parseXML (objectPath_ "/")
"<node>\
\ <interface name='com.example.Foo'>\
\ <method name='Foo'>\
\ <arg type='yy'/>\
\ </method>\
\ </interface>\
\</node>"
test_XmlWriteFailed :: TestTree
test_XmlWriteFailed = testCase "xml-write-failed" $ do
-- child's object path isn't under parent's
Nothing @=? Introspection.formatXML (Introspection.object (objectPath_ "/foo"))
{ Introspection.objectChildren =
[ Introspection.object (objectPath_ "/bar")
]
}
-- invalid type
Nothing @=? Introspection.formatXML (Introspection.object (objectPath_ "/foo"))
{ Introspection.objectInterfaces =
[ (Introspection.interface (interfaceName_ "/bar"))
{ Introspection.interfaceProperties =
[ Introspection.property "prop" (TypeDictionary TypeVariant TypeVariant)
]
}
]
}
instance Arbitrary Type where
arbitrary = oneof [atom, container] where
atom = elements
[ TypeBoolean
, TypeWord8
, TypeWord16
, TypeWord32
, TypeWord64
, TypeInt16
, TypeInt32
, TypeInt64
, TypeDouble
, TypeString
, TypeObjectPath
, TypeSignature
]
container = oneof
[ return TypeVariant
, liftM TypeArray arbitrary
, liftM2 TypeDictionary atom arbitrary
, liftM TypeStructure (listOf1 (halfSized arbitrary))
]
instance Arbitrary Introspection.Object where
arbitrary = arbitrary >>= subObject
subObject :: ObjectPath -> Gen Introspection.Object
subObject parentPath = sized $ \n -> resize (min n 4) $ do
let nonRoot = do
x <- resize 10 arbitrary
case formatObjectPath x of
"/" -> nonRoot
x' -> return x'
thisPath <- nonRoot
let path' = case formatObjectPath parentPath of
"/" -> thisPath
x -> x ++ thisPath
let path = objectPath_ path'
ifaces <- arbitrary
children <- halfSized (listOf (subObject path))
return (Introspection.object path)
{ Introspection.objectInterfaces = ifaces
, Introspection.objectChildren = children
}
instance Arbitrary Introspection.Interface where
arbitrary = do
name <- arbitrary
methods <- arbitrary
signals <- arbitrary
properties <- arbitrary
return (Introspection.interface name)
{ Introspection.interfaceMethods = methods
, Introspection.interfaceSignals = signals
, Introspection.interfaceProperties = properties
}
instance Arbitrary Introspection.Method where
arbitrary = do
name <- arbitrary
args <- arbitrary
return (Introspection.method name)
{ Introspection.methodArgs = args
}
instance Arbitrary Introspection.Signal where
arbitrary = do
name <- arbitrary
args <- arbitrary
return (Introspection.signal name)
{ Introspection.signalArgs = args
}
instance Arbitrary Introspection.MethodArg where
arbitrary = Introspection.methodArg
<$> gen_Ascii
<*> arbitrary
<*> arbitrary
instance Arbitrary Introspection.Direction where
arbitrary = elements [Introspection.directionIn, Introspection.directionOut]
instance Arbitrary Introspection.SignalArg where
arbitrary = Introspection.signalArg
<$> gen_Ascii
<*> arbitrary
instance Arbitrary Introspection.Property where
arbitrary = do
name <- gen_Ascii
t <- arbitrary
canRead <- arbitrary
canWrite <- arbitrary
return (Introspection.property name t)
{ Introspection.propertyRead = canRead
, Introspection.propertyWrite = canWrite
}
gen_Ascii :: Gen String
gen_Ascii = listOf (elements ['!'..'~'])