<commit_before>import unittest

import logging

_logger = logging.getLogger(__name__)

from tempfile import mkdtemp
from rdflib import *
from rdflib.Graph import Graph

class ContextTestCase(unittest.TestCase):
    #store = 'Memory'
    store = 'default'

    def setUp(self):
        self.graph = ConjunctiveGraph(store=self.store)
        if self.store == "MySQL":
            from mysql import configString
            from rdflib.store.MySQL import MySQL
            path=configString
            MySQL().destroy(path)
        else:
            path = a_tmp_dir = mkdtemp()
        self.graph.open(path, create=True)
        self.michel = URIRef(u'michel')
        self.tarek = URIRef(u'tarek')
        self.bob = URIRef(u'bob')
        self.likes = URIRef(u'likes')
        self.hates = URIRef(u'hates')
        self.pizza = URIRef(u'pizza')
        self.cheese = URIRef(u'cheese')

        self.c1 = URIRef(u'context-1')
        self.c2 = URIRef(u'context-2')

        # delete the graph for each test!
        self.graph.remove((None, None, None))

    def tearDown(self):
        self.graph.close()

    def get_context(self, identifier):
        assert isinstance(identifier, URIRef) or \
               isinstance(identifier, BNode), type(identifier)
        return Graph(store=self.graph.store, identifier=identifier,
                         namespace_manager=self)
    def addStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        graph = Graph(self.graph.store, c1)

        graph.add((tarek, likes, pizza))
        graph.add((tarek, likes, cheese))
        graph.add((michel, likes, pizza))
        graph.add((michel, likes, cheese))
        graph.add((bob, likes, cheese))
        graph.add((bob, hates, pizza))
        graph.add((bob, hates, michel)) # gasp!

    def removeStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        graph = Graph(self.graph.store, c1)

        graph.remove((tarek, likes, pizza))
        graph.remove((tarek, likes, cheese))
        graph.remove((michel, likes, pizza))
        graph.remove((michel, likes, cheese))
        graph.remove((bob, likes, cheese))
        graph.remove((bob, hates, pizza))
        graph.remove((bob, hates, michel)) # gasp!

    def addStuffInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        # add to default context
        self.graph.add(triple)
        # add to context 1
        graph = Graph(self.graph.store, c1)
        graph.add(triple)
        # add to context 2
        graph = Graph(self.graph.store, c2)
        graph.add(triple)

    def testConjunction(self):
        self.addStuffInMultipleContexts()
        triple = (self.pizza, self.likes, self.pizza)
        # add to context 1
        graph = Graph(self.graph.store, self.c1)
        graph.add(triple)
        self.assertEquals(len(self.graph), len(graph))

    def testAdd(self):
        self.addStuff()

    def testRemove(self):
        self.addStuff()
        self.removeStuff()

    def testLenInOneContext(self):
        c1 = self.c1
        # make sure context is empty

        self.graph.remove_context(self.get_context(c1))
        graph = Graph(self.graph.store, c1)
        oldLen = len(self.graph)

        for i in range(0, 10):
            graph.add((BNode(), self.hates, self.hates))
        self.assertEquals(len(graph), oldLen + 10)
        self.assertEquals(len(self.get_context(c1)), oldLen + 10)
        self.graph.remove_context(self.get_context(c1))
        self.assertEquals(len(self.graph), oldLen)
        self.assertEquals(len(graph), 0)

    def testLenInMultipleContexts(self):
        oldLen = len(self.graph)
        self.addStuffInMultipleContexts()

        # addStuffInMultipleContexts is adding the same triple to
        # three different contexts. So it's only + 1
        self.assertEquals(len(self.graph), oldLen + 1)

        graph = Graph(self.graph.store, self.c1)
        self.assertEquals(len(graph), oldLen + 1)

    def testRemoveInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()

        # triple should be still in store after removing it from c1 + c2
        self.assert_(triple in self.graph)
        graph = Graph(self.graph.store, c1)
        graph.remove(triple)
        self.assert_(triple in self.graph)
        graph = Graph(self.graph.store, c2)
        graph.remove(triple)
        self.assert_(triple in self.graph)
        self.graph.remove(triple)
        # now gone!
        self.assert_(triple not in self.graph)

        # add again and see if remove without context removes all triples!
        self.addStuffInMultipleContexts()
        self.graph.remove(triple)
        self.assert_(triple not in self.graph)

    def testContexts(self):
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()
        def cid(c):
            return c.identifier
        self.assert_(self.c1 in map(cid, self.graph.contexts()))
        self.assert_(self.c2 in map(cid, self.graph.contexts()))

        contextList = map(cid, list(self.graph.contexts(triple)))
        self.assert_(self.c1 in contextList)
        self.assert_(self.c2 in contextList)

    def testRemoveContext(self):
        c1 = self.c1

        self.addStuffInMultipleContexts()
        self.assertEquals(len(Graph(self.graph.store, c1)), 1)
        self.assertEquals(len(self.get_context(c1)), 1)

        self.graph.remove_context(self.get_context(c1))
        self.assert_(self.c1 not in self.graph.contexts())

    def testRemoveAny(self):
        Any = None
        self.addStuffInMultipleContexts()
        self.graph.remove((Any, Any, Any))
        self.assertEquals(len(self.graph), 0)

    def testTriples(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        asserte = self.assertEquals
        triples = self.graph.triples
        graph = self.graph
        c1graph = Graph(self.graph.store, c1)
        c1triples = c1graph.triples
        Any = None

        self.addStuff()

        # unbound subjects with context
        asserte(len(list(c1triples((Any, likes, pizza)))), 2)
        asserte(len(list(c1triples((Any, hates, pizza)))), 1)
        asserte(len(list(c1triples((Any, likes, cheese)))), 3)
        asserte(len(list(c1triples((Any, hates, cheese)))), 0)

        # unbound subjects without context, same results!
        asserte(len(list(triples((Any, likes, pizza)))), 2)
        asserte(len(list(triples((Any, hates, pizza)))), 1)
        asserte(len(list(triples((Any, likes, cheese)))), 3)
        asserte(len(list(triples((Any, hates, cheese)))), 0)

        # unbound objects with context
        asserte(len(list(c1triples((michel, likes, Any)))), 2)
        asserte(len(list(c1triples((tarek, likes, Any)))), 2)
        asserte(len(list(c1triples((bob, hates, Any)))), 2)
        asserte(len(list(c1triples((bob, likes, Any)))), 1)

        # unbound objects without context, same results!
        asserte(len(list(triples((michel, likes, Any)))), 2)
        asserte(len(list(triples((tarek, likes, Any)))), 2)
        asserte(len(list(triples((bob, hates, Any)))), 2)
        asserte(len(list(triples((bob, likes, Any)))), 1)

        # unbound predicates with context
        asserte(len(list(c1triples((michel, Any, cheese)))), 1)
        asserte(len(list(c1triples((tarek, Any, cheese)))), 1)
        asserte(len(list(c1triples((bob, Any, pizza)))), 1)
        asserte(len(list(c1triples((bob, Any, michel)))), 1)

        # unbound predicates without context, same results!
        asserte(len(list(triples((michel, Any, cheese)))), 1)
        asserte(len(list(triples((tarek, Any, cheese)))), 1)
        asserte(len(list(triples((bob, Any, pizza)))), 1)
        asserte(len(list(triples((bob, Any, michel)))), 1)

        # unbound subject, objects with context
        asserte(len(list(c1triples((Any, hates, Any)))), 2)
        asserte(len(list(c1triples((Any, likes, Any)))), 5)

        # unbound subject, objects without context, same results!
        asserte(len(list(triples((Any, hates, Any)))), 2)
        asserte(len(list(triples((Any, likes, Any)))), 5)

        # unbound predicates, objects with context
        asserte(len(list(c1triples((michel, Any, Any)))), 2)
        asserte(len(list(c1triples((bob, Any, Any)))), 3)
        asserte(len(list(c1triples((tarek, Any, Any)))), 2)

        # unbound predicates, objects without context, same results!
        asserte(len(list(triples((michel, Any, Any)))), 2)
        asserte(len(list(triples((bob, Any, Any)))), 3)
        asserte(len(list(triples((tarek, Any, Any)))), 2)

        # unbound subjects, predicates with context
        asserte(len(list(c1triples((Any, Any, pizza)))), 3)
        asserte(len(list(c1triples((Any, Any, cheese)))), 3)
        asserte(len(list(c1triples((Any, Any, michel)))), 1)

        # unbound subjects, predicates without context, same results!
        asserte(len(list(triples((Any, Any, pizza)))), 3)
        asserte(len(list(triples((Any, Any, cheese)))), 3)
        asserte(len(list(triples((Any, Any, michel)))), 1)

        # all unbound with context
        asserte(len(list(c1triples((Any, Any, Any)))), 7)
        # all unbound without context, same result!
        asserte(len(list(triples((Any, Any, Any)))), 7)

        for c in [graph, self.get_context(c1)]:
            # unbound subjects
            asserte(set(c.subjects(likes, pizza)), set((michel, tarek)))
            asserte(set(c.subjects(hates, pizza)), set((bob,)))
            asserte(set(c.subjects(likes, cheese)), set([tarek, bob, michel]))
            asserte(set(c.subjects(hates, cheese)), set())

            # unbound objects
            asserte(set(c.objects(michel, likes)), set([cheese, pizza]))
            asserte(set(c.objects(tarek, likes)), set([cheese, pizza]))
            asserte(set(c.objects(bob, hates)), set([michel, pizza]))
            asserte(set(c.objects(bob, likes)), set([cheese]))

            # unbound predicates
            asserte(set(c.predicates(michel, cheese)), set([likes]))
            asserte(set(c.predicates(tarek, cheese)), set([likes]))
            asserte(set(c.predicates(bob, pizza)), set([hates]))
            asserte(set(c.predicates(bob, michel)), set([hates]))

            asserte(set(c.subject_objects(hates)), set([(bob, pizza), (bob, michel)]))
            asserte(set(c.subject_objects(likes)), set([(tarek, cheese), (michel, cheese), (michel, pizza), (bob, cheese), (tarek, pizza)]))

            asserte(set(c.predicate_objects(michel)), set([(likes, cheese), (likes, pizza)]))
            asserte(set(c.predicate_objects(bob)), set([(likes, cheese), (hates, pizza), (hates, michel)]))
            asserte(set(c.predicate_objects(tarek)), set([(likes, cheese), (likes, pizza)]))

            asserte(set(c.subject_predicates(pizza)), set([(bob, hates), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(cheese)), set([(bob, likes), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(michel)), set([(bob, hates)]))

            asserte(set(c), set([(bob, hates, michel), (bob, likes, cheese), (tarek, likes, pizza), (michel, likes, pizza), (michel, likes, cheese), (bob, hates, pizza), (tarek, likes, cheese)]))

        # remove stuff and make sure the graph is empty again
        self.removeStuff()
        asserte(len(list(c1triples((Any, Any, Any)))), 0)
        asserte(len(list(triples((Any, Any, Any)))), 0)

class IOMemoryContextTestCase(ContextTestCase):
    store = "IOMemory"

try:
    import persistent
    # If we can import persistent then test ZODB store
    class ZODBContextTestCase(ContextTestCase):
        store = "ZODB"
except ImportError, e:
    _logger.warning("Can not test ZODB store: %s" % e)

try:
    import MySQLdb
    # If we can import RDF then test Redland store
    class MySQLContextTestCase(ContextTestCase):
        store = "MySQL"
except ImportError, e:
    _logger.warning("Can not test MySQL store: %s" % e)

try:
    import RDF
    # If we can import RDF then test Redland store
    class RedlandContextTestCase(ContextTestCase):
        store = "Redland"
except ImportError, e:
    _logger.warning("Can not test Redland store: %s" % e)

del ContextTestCase

if __name__ == '__main__':
    unittest.main()
<commit_msg>Split out Sleepycat tests; made them part of 'not quick'<commit_after>import unittest

import logging

_logger = logging.getLogger(__name__)

from tempfile import mkdtemp
from rdflib import *
from rdflib.Graph import Graph

class ContextTestCase(unittest.TestCase):
    #store = 'Memory'
    store = 'default'

    def setUp(self):
        self.graph = ConjunctiveGraph(store=self.store)
        if self.store == "MySQL":
            from mysql import configString
            from rdflib.store.MySQL import MySQL
            path=configString
            MySQL().destroy(path)
        else:
            path = a_tmp_dir = mkdtemp()
        self.graph.open(path, create=True)
        self.michel = URIRef(u'michel')
        self.tarek = URIRef(u'tarek')
        self.bob = URIRef(u'bob')
        self.likes = URIRef(u'likes')
        self.hates = URIRef(u'hates')
        self.pizza = URIRef(u'pizza')
        self.cheese = URIRef(u'cheese')

        self.c1 = URIRef(u'context-1')
        self.c2 = URIRef(u'context-2')

        # delete the graph for each test!
        self.graph.remove((None, None, None))

    def tearDown(self):
        self.graph.close()

    def get_context(self, identifier):
        assert isinstance(identifier, URIRef) or \
               isinstance(identifier, BNode), type(identifier)
        return Graph(store=self.graph.store, identifier=identifier,
                         namespace_manager=self)
    def addStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        graph = Graph(self.graph.store, c1)

        graph.add((tarek, likes, pizza))
        graph.add((tarek, likes, cheese))
        graph.add((michel, likes, pizza))
        graph.add((michel, likes, cheese))
        graph.add((bob, likes, cheese))
        graph.add((bob, hates, pizza))
        graph.add((bob, hates, michel)) # gasp!

    def removeStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        graph = Graph(self.graph.store, c1)

        graph.remove((tarek, likes, pizza))
        graph.remove((tarek, likes, cheese))
        graph.remove((michel, likes, pizza))
        graph.remove((michel, likes, cheese))
        graph.remove((bob, likes, cheese))
        graph.remove((bob, hates, pizza))
        graph.remove((bob, hates, michel)) # gasp!

    def addStuffInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        # add to default context
        self.graph.add(triple)
        # add to context 1
        graph = Graph(self.graph.store, c1)
        graph.add(triple)
        # add to context 2
        graph = Graph(self.graph.store, c2)
        graph.add(triple)

    def testConjunction(self):
        self.addStuffInMultipleContexts()
        triple = (self.pizza, self.likes, self.pizza)
        # add to context 1
        graph = Graph(self.graph.store, self.c1)
        graph.add(triple)
        self.assertEquals(len(self.graph), len(graph))

    def testAdd(self):
        self.addStuff()

    def testRemove(self):
        self.addStuff()
        self.removeStuff()

    def testLenInOneContext(self):
        c1 = self.c1
        # make sure context is empty

        self.graph.remove_context(self.get_context(c1))
        graph = Graph(self.graph.store, c1)
        oldLen = len(self.graph)

        for i in range(0, 10):
            graph.add((BNode(), self.hates, self.hates))
        self.assertEquals(len(graph), oldLen + 10)
        self.assertEquals(len(self.get_context(c1)), oldLen + 10)
        self.graph.remove_context(self.get_context(c1))
        self.assertEquals(len(self.graph), oldLen)
        self.assertEquals(len(graph), 0)

    def testLenInMultipleContexts(self):
        oldLen = len(self.graph)
        self.addStuffInMultipleContexts()

        # addStuffInMultipleContexts is adding the same triple to
        # three different contexts. So it's only + 1
        self.assertEquals(len(self.graph), oldLen + 1)

        graph = Graph(self.graph.store, self.c1)
        self.assertEquals(len(graph), oldLen + 1)

    def testRemoveInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()

        # triple should be still in store after removing it from c1 + c2
        self.assert_(triple in self.graph)
        graph = Graph(self.graph.store, c1)
        graph.remove(triple)
        self.assert_(triple in self.graph)
        graph = Graph(self.graph.store, c2)
        graph.remove(triple)
        self.assert_(triple in self.graph)
        self.graph.remove(triple)
        # now gone!
        self.assert_(triple not in self.graph)

        # add again and see if remove without context removes all triples!
        self.addStuffInMultipleContexts()
        self.graph.remove(triple)
        self.assert_(triple not in self.graph)

    def testContexts(self):
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()
        def cid(c):
            return c.identifier
        self.assert_(self.c1 in map(cid, self.graph.contexts()))
        self.assert_(self.c2 in map(cid, self.graph.contexts()))

        contextList = map(cid, list(self.graph.contexts(triple)))
        self.assert_(self.c1 in contextList)
        self.assert_(self.c2 in contextList)

    def testRemoveContext(self):
        c1 = self.c1

        self.addStuffInMultipleContexts()
        self.assertEquals(len(Graph(self.graph.store, c1)), 1)
        self.assertEquals(len(self.get_context(c1)), 1)

        self.graph.remove_context(self.get_context(c1))
        self.assert_(self.c1 not in self.graph.contexts())

    def testRemoveAny(self):
        Any = None
        self.addStuffInMultipleContexts()
        self.graph.remove((Any, Any, Any))
        self.assertEquals(len(self.graph), 0)

    def testTriples(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        asserte = self.assertEquals
        triples = self.graph.triples
        graph = self.graph
        c1graph = Graph(self.graph.store, c1)
        c1triples = c1graph.triples
        Any = None

        self.addStuff()

        # unbound subjects with context
        asserte(len(list(c1triples((Any, likes, pizza)))), 2)
        asserte(len(list(c1triples((Any, hates, pizza)))), 1)
        asserte(len(list(c1triples((Any, likes, cheese)))), 3)
        asserte(len(list(c1triples((Any, hates, cheese)))), 0)

        # unbound subjects without context, same results!
        asserte(len(list(triples((Any, likes, pizza)))), 2)
        asserte(len(list(triples((Any, hates, pizza)))), 1)
        asserte(len(list(triples((Any, likes, cheese)))), 3)
        asserte(len(list(triples((Any, hates, cheese)))), 0)

        # unbound objects with context
        asserte(len(list(c1triples((michel, likes, Any)))), 2)
        asserte(len(list(c1triples((tarek, likes, Any)))), 2)
        asserte(len(list(c1triples((bob, hates, Any)))), 2)
        asserte(len(list(c1triples((bob, likes, Any)))), 1)

        # unbound objects without context, same results!
        asserte(len(list(triples((michel, likes, Any)))), 2)
        asserte(len(list(triples((tarek, likes, Any)))), 2)
        asserte(len(list(triples((bob, hates, Any)))), 2)
        asserte(len(list(triples((bob, likes, Any)))), 1)

        # unbound predicates with context
        asserte(len(list(c1triples((michel, Any, cheese)))), 1)
        asserte(len(list(c1triples((tarek, Any, cheese)))), 1)
        asserte(len(list(c1triples((bob, Any, pizza)))), 1)
        asserte(len(list(c1triples((bob, Any, michel)))), 1)

        # unbound predicates without context, same results!
        asserte(len(list(triples((michel, Any, cheese)))), 1)
        asserte(len(list(triples((tarek, Any, cheese)))), 1)
        asserte(len(list(triples((bob, Any, pizza)))), 1)
        asserte(len(list(triples((bob, Any, michel)))), 1)

        # unbound subject, objects with context
        asserte(len(list(c1triples((Any, hates, Any)))), 2)
        asserte(len(list(c1triples((Any, likes, Any)))), 5)

        # unbound subject, objects without context, same results!
        asserte(len(list(triples((Any, hates, Any)))), 2)
        asserte(len(list(triples((Any, likes, Any)))), 5)

        # unbound predicates, objects with context
        asserte(len(list(c1triples((michel, Any, Any)))), 2)
        asserte(len(list(c1triples((bob, Any, Any)))), 3)
        asserte(len(list(c1triples((tarek, Any, Any)))), 2)

        # unbound predicates, objects without context, same results!
        asserte(len(list(triples((michel, Any, Any)))), 2)
        asserte(len(list(triples((bob, Any, Any)))), 3)
        asserte(len(list(triples((tarek, Any, Any)))), 2)

        # unbound subjects, predicates with context
        asserte(len(list(c1triples((Any, Any, pizza)))), 3)
        asserte(len(list(c1triples((Any, Any, cheese)))), 3)
        asserte(len(list(c1triples((Any, Any, michel)))), 1)

        # unbound subjects, predicates without context, same results!
        asserte(len(list(triples((Any, Any, pizza)))), 3)
        asserte(len(list(triples((Any, Any, cheese)))), 3)
        asserte(len(list(triples((Any, Any, michel)))), 1)

        # all unbound with context
        asserte(len(list(c1triples((Any, Any, Any)))), 7)
        # all unbound without context, same result!
        asserte(len(list(triples((Any, Any, Any)))), 7)

        for c in [graph, self.get_context(c1)]:
            # unbound subjects
            asserte(set(c.subjects(likes, pizza)), set((michel, tarek)))
            asserte(set(c.subjects(hates, pizza)), set((bob,)))
            asserte(set(c.subjects(likes, cheese)), set([tarek, bob, michel]))
            asserte(set(c.subjects(hates, cheese)), set())

            # unbound objects
            asserte(set(c.objects(michel, likes)), set([cheese, pizza]))
            asserte(set(c.objects(tarek, likes)), set([cheese, pizza]))
            asserte(set(c.objects(bob, hates)), set([michel, pizza]))
            asserte(set(c.objects(bob, likes)), set([cheese]))

            # unbound predicates
            asserte(set(c.predicates(michel, cheese)), set([likes]))
            asserte(set(c.predicates(tarek, cheese)), set([likes]))
            asserte(set(c.predicates(bob, pizza)), set([hates]))
            asserte(set(c.predicates(bob, michel)), set([hates]))

            asserte(set(c.subject_objects(hates)), set([(bob, pizza), (bob, michel)]))
            asserte(set(c.subject_objects(likes)), set([(tarek, cheese), (michel, cheese), (michel, pizza), (bob, cheese), (tarek, pizza)]))

            asserte(set(c.predicate_objects(michel)), set([(likes, cheese), (likes, pizza)]))
            asserte(set(c.predicate_objects(bob)), set([(likes, cheese), (hates, pizza), (hates, michel)]))
            asserte(set(c.predicate_objects(tarek)), set([(likes, cheese), (likes, pizza)]))

            asserte(set(c.subject_predicates(pizza)), set([(bob, hates), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(cheese)), set([(bob, likes), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(michel)), set([(bob, hates)]))

            asserte(set(c), set([(bob, hates, michel), (bob, likes, cheese), (tarek, likes, pizza), (michel, likes, pizza), (michel, likes, cheese), (bob, hates, pizza), (tarek, likes, cheese)]))

        # remove stuff and make sure the graph is empty again
        self.removeStuff()
        asserte(len(list(c1triples((Any, Any, Any)))), 0)
        asserte(len(list(triples((Any, Any, Any)))), 0)

# tested via ContextTestCase
#class IOMemoryContextTestCase(ContextTestCase):
#    store = "IOMemory"

try:
    import persistent
    # If we can import persistent then test ZODB store
    class ZODBContextTestCase(ContextTestCase):
        store = "ZODB"
except ImportError, e:
    _logger.warning("Can not test ZODB store: %s" % e)

try:
    import MySQLdb
    # If we can import RDF then test Redland store
    class MySQLContextTestCase(ContextTestCase):
        store = "MySQL"
except ImportError, e:
    _logger.warning("Can not test MySQL store: %s" % e)

try:
    import RDF
    # If we can import RDF then test Redland store
    class RedlandContextTestCase(ContextTestCase):
        store = "Redland"
except ImportError, e:
    _logger.warning("Can not test Redland store: %s" % e)

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>import unittest

from rdflib import *

class ContextTestCase(unittest.TestCase):
    backend = 'Sleepycat'
    path = 'store'

    def setUp(self):
        self.graph = Graph(backend=self.backend)
        self.graph.open(self.path)
        self.michel = URIRef(u'michel')
        self.tarek = URIRef(u'tarek')
        self.bob = URIRef(u'bob')
        self.likes = URIRef(u'likes')
        self.hates = URIRef(u'hates')
        self.pizza = URIRef(u'pizza')
        self.cheese = URIRef(u'cheese')

        self.c1 = URIRef(u'context-1')
        self.c2 = URIRef(u'context-2')

        # delete the graph for each test!
        self.graph.remove((None, None, None))

    def tearDown(self):
        self.graph.close()

    def addStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1

        self.graph.add((tarek, likes, pizza), c1)
        self.graph.add((tarek, likes, cheese), c1)
        self.graph.add((michel, likes, pizza), c1)
        self.graph.add((michel, likes, cheese), c1)
        self.graph.add((bob, likes, cheese), c1)
        self.graph.add((bob, hates, pizza), c1)
        self.graph.add((bob, hates, michel), c1) # gasp!

    def removeStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1

        self.graph.remove((tarek, likes, pizza), c1)
        self.graph.remove((tarek, likes, cheese), c1)
        self.graph.remove((michel, likes, pizza), c1)
        self.graph.remove((michel, likes, cheese), c1)
        self.graph.remove((bob, likes, cheese), c1)
        self.graph.remove((bob, hates, pizza), c1)
        self.graph.remove((bob, hates, michel), c1) # gasp!

    def addStuffInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        # add to default context
        self.graph.add(triple)
        # add to context 1
        self.graph.add(triple, c1)
        # add to context 2
        self.graph.add(triple, c2)

    def testAdd(self):
        self.addStuff()

    def testRemove(self):
        self.addStuff()
        self.removeStuff()

    def testLenInOneContext(self):
        c1 = self.c1
        oldLen = len(self.graph)
        # make sure context is empty
        try:
            self.graph.remove_context(c1)
        except:
            pass

        for i in range(0, 10):
            self.graph.add((BNode(), self.hates, self.hates), c1)
        self.assertEquals(len(self.graph), oldLen + 10)
        self.graph.remove_context(c1)
        self.assertEquals(len(self.graph), oldLen)

    def testLenInMultipleContexts(self):
        oldLen = len(self.graph)
        self.addStuffInMultipleContexts()
        # TODO: what should the length of the graph now be? 1 or 3?
        #self.assertEquals(len(self.graph), oldLen + 1)

    def testRemoveInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()

        # triple should be still in store after removing it from c1 + c2
        self.assert_(triple in self.graph)
        self.graph.remove(triple, c1)
        self.assert_(triple in self.graph)
        self.graph.remove(triple, c2)
        self.assert_(triple in self.graph)
        self.graph.remove(triple)
        # now gone!
        self.assert_(triple not in self.graph)

        # add again and see if remove without context removes all triples!
        self.addStuffInMultipleContexts()
        self.graph.remove(triple)
        self.assert_(triple not in self.graph)

    def testContexts(self):
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()
        self.assert_(self.c1 in self.graph.contexts())
        self.assert_(self.c2 in self.graph.contexts())

        contextList = list(self.graph.contexts(triple))
        self.assert_(self.c1 in contextList)
        self.assert_(self.c2 in contextList)

    def testRemoveContext(self):
        c1 = self.c1

        self.addStuffInMultipleContexts()
        self.assert_(self.graph.__len__(context=c1), 1)

        self.graph.remove_context(c1)
        self.assert_(self.c1 not in self.graph.contexts())

    def testRemoveAny(self):
        Any = None
        self.addStuffInMultipleContexts()
        self.graph.remove((Any, Any, Any))
        self.assertEquals(len(self.graph), 0)

    def testTriples(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        asserte = self.assertEquals
        triples = self.graph.triples
        graph = self.graph
        Any = None

        self.addStuff()

        # unbound subjects with context
        asserte(len(list(triples((Any, likes, pizza), c1))), 2)
        asserte(len(list(triples((Any, hates, pizza), c1))), 1)
        asserte(len(list(triples((Any, likes, cheese), c1))), 3)
        asserte(len(list(triples((Any, hates, cheese), c1))), 0)

        # unbound subjects without context, same results!
        asserte(len(list(triples((Any, likes, pizza)))), 2)
        asserte(len(list(triples((Any, hates, pizza)))), 1)
        asserte(len(list(triples((Any, likes, cheese)))), 3)
        asserte(len(list(triples((Any, hates, cheese)))), 0)

        # unbound objects with context
        asserte(len(list(triples((michel, likes, Any), c1))), 2)
        asserte(len(list(triples((tarek, likes, Any), c1))), 2)
        asserte(len(list(triples((bob, hates, Any), c1))), 2)
        asserte(len(list(triples((bob, likes, Any), c1))), 1)

        # unbound objects without context, same results!
        asserte(len(list(triples((michel, likes, Any)))), 2)
        asserte(len(list(triples((tarek, likes, Any)))), 2)
        asserte(len(list(triples((bob, hates, Any)))), 2)
        asserte(len(list(triples((bob, likes, Any)))), 1)

        # unbound predicates with context
        asserte(len(list(triples((michel, Any, cheese), c1))), 1)
        asserte(len(list(triples((tarek, Any, cheese), c1))), 1)
        asserte(len(list(triples((bob, Any, pizza), c1))), 1)
        asserte(len(list(triples((bob, Any, michel), c1))), 1)

        # unbound predicates without context, same results!
        asserte(len(list(triples((michel, Any, cheese)))), 1)
        asserte(len(list(triples((tarek, Any, cheese)))), 1)
        asserte(len(list(triples((bob, Any, pizza)))), 1)
        asserte(len(list(triples((bob, Any, michel)))), 1)

        # unbound subject, objects with context
        asserte(len(list(triples((Any, hates, Any), c1))), 2)
        asserte(len(list(triples((Any, likes, Any), c1))), 5)

        # unbound subject, objects without context, same results!
        asserte(len(list(triples((Any, hates, Any)))), 2)
        asserte(len(list(triples((Any, likes, Any)))), 5)

        # unbound predicates, objects with context
        asserte(len(list(triples((michel, Any, Any), c1))), 2)
        asserte(len(list(triples((bob, Any, Any), c1))), 3)
        asserte(len(list(triples((tarek, Any, Any), c1))), 2)

        # unbound predicates, objects without context, same results!
        asserte(len(list(triples((michel, Any, Any)))), 2)
        asserte(len(list(triples((bob, Any, Any)))), 3)
        asserte(len(list(triples((tarek, Any, Any)))), 2)

        # unbound subjects, predicates with context
        asserte(len(list(triples((Any, Any, pizza), c1))), 3)
        asserte(len(list(triples((Any, Any, cheese), c1))), 3)
        asserte(len(list(triples((Any, Any, michel), c1))), 1)

        # unbound subjects, predicates without context, same results!
        asserte(len(list(triples((Any, Any, pizza)))), 3)
        asserte(len(list(triples((Any, Any, cheese)))), 3)
        asserte(len(list(triples((Any, Any, michel)))), 1)

        # all unbound with context
        asserte(len(list(triples((Any, Any, Any), c1))), 7)
        # all unbound without context, same result!
        asserte(len(list(triples((Any, Any, Any)))), 7)

        for c in [graph, graph.get_context(c1)]:
            # unbound subjects
            c = graph.get_context(c1)
            asserte(set(c.subjects(likes, pizza)), set((michel, tarek)))
            asserte(set(c.subjects(hates, pizza)), set((bob,)))
            asserte(set(c.subjects(likes, cheese)), set([tarek, bob, michel]))
            asserte(set(c.subjects(hates, cheese)), set())

            # unbound objects
            asserte(set(c.objects(michel, likes)), set([cheese, pizza]))
            asserte(set(c.objects(tarek, likes)), set([cheese, pizza]))
            asserte(set(c.objects(bob, hates)), set([michel, pizza]))
            asserte(set(c.objects(bob, likes)), set([cheese]))

            # unbound predicates
            asserte(set(c.predicates(michel, cheese)), set([likes]))
            asserte(set(c.predicates(tarek, cheese)), set([likes]))
            asserte(set(c.predicates(bob, pizza)), set([hates]))
            asserte(set(c.predicates(bob, michel)), set([hates]))

            asserte(set(c.subject_objects(hates)), set([(bob, pizza), (bob, michel)]))
            asserte(set(c.subject_objects(likes)), set([(tarek, cheese), (michel, cheese), (michel, pizza), (bob, cheese), (tarek, pizza)]))

            asserte(set(c.predicate_objects(michel)), set([(likes, cheese), (likes, pizza)]))
            asserte(set(c.predicate_objects(bob)), set([(likes, cheese), (hates, pizza), (hates, michel)]))
            asserte(set(c.predicate_objects(tarek)), set([(likes, cheese), (likes, pizza)]))

            asserte(set(c.subject_predicates(pizza)), set([(bob, hates), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(cheese)), set([(bob, likes), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(michel)), set([(bob, hates)]))

            asserte(set(c.triples((None, None, None))), set([(bob, hates, michel), (bob, likes, cheese), (tarek, likes, pizza), (michel, likes, pizza), (michel, likes, cheese), (bob, hates, pizza), (tarek, likes, cheese)]))

        # remove stuff and make sure the graph is empty again
        self.removeStuff()
        asserte(len(list(triples((Any, Any, Any), c1))), 0)
        asserte(len(list(triples((Any, Any, Any)))), 0)

class IOMemoryContextTestCase(ContextTestCase):
    backend = "IOMemory"

try:
    from rdflib.store.Sleepycat import Sleepycat
    class SleepycatStoreTestCase(ContextTestCase):
        backend = "Sleepycat"
except ImportError, e:
    print "Can not test Sleepycat store:", e

try:
    import persistent
    # If we can import persistent then test ZODB backend
    class ZODBContextTestCase(ContextTestCase):
        backend = "ZODB"
except ImportError:
    pass

try:
    import backends.Sleepycat
except ImportError:
    del ContextTestCase

if __name__ == '__main__':
    unittest.main()
<commit_msg>updated to use temp path<commit_after>import unittest

from tempfile import mkdtemp
from rdflib import *

class ContextTestCase(unittest.TestCase):
    backend = 'Memory'

    def setUp(self):
        self.graph = Graph(backend=self.backend)
        self.graph.open(mkdtemp())
        self.michel = URIRef(u'michel')
        self.tarek = URIRef(u'tarek')
        self.bob = URIRef(u'bob')
        self.likes = URIRef(u'likes')
        self.hates = URIRef(u'hates')
        self.pizza = URIRef(u'pizza')
        self.cheese = URIRef(u'cheese')

        self.c1 = URIRef(u'context-1')
        self.c2 = URIRef(u'context-2')

        # delete the graph for each test!
        self.graph.remove((None, None, None))

    def tearDown(self):
        self.graph.close()

    def addStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1

        self.graph.add((tarek, likes, pizza), c1)
        self.graph.add((tarek, likes, cheese), c1)
        self.graph.add((michel, likes, pizza), c1)
        self.graph.add((michel, likes, cheese), c1)
        self.graph.add((bob, likes, cheese), c1)
        self.graph.add((bob, hates, pizza), c1)
        self.graph.add((bob, hates, michel), c1) # gasp!

    def removeStuff(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1

        self.graph.remove((tarek, likes, pizza), c1)
        self.graph.remove((tarek, likes, cheese), c1)
        self.graph.remove((michel, likes, pizza), c1)
        self.graph.remove((michel, likes, cheese), c1)
        self.graph.remove((bob, likes, cheese), c1)
        self.graph.remove((bob, hates, pizza), c1)
        self.graph.remove((bob, hates, michel), c1) # gasp!

    def addStuffInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        # add to default context
        self.graph.add(triple)
        # add to context 1
        self.graph.add(triple, c1)
        # add to context 2
        self.graph.add(triple, c2)

    def testAdd(self):
        self.addStuff()

    def testRemove(self):
        self.addStuff()
        self.removeStuff()

    def testLenInOneContext(self):
        c1 = self.c1
        oldLen = len(self.graph)
        # make sure context is empty
        try:
            self.graph.remove_context(c1)
        except:
            pass

        for i in range(0, 10):
            self.graph.add((BNode(), self.hates, self.hates), c1)
        self.assertEquals(len(self.graph), oldLen + 10)
        self.graph.remove_context(c1)
        self.assertEquals(len(self.graph), oldLen)

    def testLenInMultipleContexts(self):
        oldLen = len(self.graph)
        self.addStuffInMultipleContexts()
        # TODO: what should the length of the graph now be? 1 or 3?
        #self.assertEquals(len(self.graph), oldLen + 1)

    def testRemoveInMultipleContexts(self):
        c1 = self.c1
        c2 = self.c2
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()

        # triple should be still in store after removing it from c1 + c2
        self.assert_(triple in self.graph)
        self.graph.remove(triple, c1)
        self.assert_(triple in self.graph)
        self.graph.remove(triple, c2)
        self.assert_(triple in self.graph)
        self.graph.remove(triple)
        # now gone!
        self.assert_(triple not in self.graph)

        # add again and see if remove without context removes all triples!
        self.addStuffInMultipleContexts()
        self.graph.remove(triple)
        self.assert_(triple not in self.graph)

    def testContexts(self):
        triple = (self.pizza, self.hates, self.tarek) # revenge!

        self.addStuffInMultipleContexts()
        self.assert_(self.c1 in self.graph.contexts())
        self.assert_(self.c2 in self.graph.contexts())

        contextList = list(self.graph.contexts(triple))
        self.assert_(self.c1 in contextList)
        self.assert_(self.c2 in contextList)

    def testRemoveContext(self):
        c1 = self.c1

        self.addStuffInMultipleContexts()
        self.assert_(self.graph.__len__(context=c1), 1)

        self.graph.remove_context(c1)
        self.assert_(self.c1 not in self.graph.contexts())

    def testRemoveAny(self):
        Any = None
        self.addStuffInMultipleContexts()
        self.graph.remove((Any, Any, Any))
        self.assertEquals(len(self.graph), 0)

    def testTriples(self):
        tarek = self.tarek
        michel = self.michel
        bob = self.bob
        likes = self.likes
        hates = self.hates
        pizza = self.pizza
        cheese = self.cheese
        c1 = self.c1
        asserte = self.assertEquals
        triples = self.graph.triples
        graph = self.graph
        Any = None

        self.addStuff()

        # unbound subjects with context
        asserte(len(list(triples((Any, likes, pizza), c1))), 2)
        asserte(len(list(triples((Any, hates, pizza), c1))), 1)
        asserte(len(list(triples((Any, likes, cheese), c1))), 3)
        asserte(len(list(triples((Any, hates, cheese), c1))), 0)

        # unbound subjects without context, same results!
        asserte(len(list(triples((Any, likes, pizza)))), 2)
        asserte(len(list(triples((Any, hates, pizza)))), 1)
        asserte(len(list(triples((Any, likes, cheese)))), 3)
        asserte(len(list(triples((Any, hates, cheese)))), 0)

        # unbound objects with context
        asserte(len(list(triples((michel, likes, Any), c1))), 2)
        asserte(len(list(triples((tarek, likes, Any), c1))), 2)
        asserte(len(list(triples((bob, hates, Any), c1))), 2)
        asserte(len(list(triples((bob, likes, Any), c1))), 1)

        # unbound objects without context, same results!
        asserte(len(list(triples((michel, likes, Any)))), 2)
        asserte(len(list(triples((tarek, likes, Any)))), 2)
        asserte(len(list(triples((bob, hates, Any)))), 2)
        asserte(len(list(triples((bob, likes, Any)))), 1)

        # unbound predicates with context
        asserte(len(list(triples((michel, Any, cheese), c1))), 1)
        asserte(len(list(triples((tarek, Any, cheese), c1))), 1)
        asserte(len(list(triples((bob, Any, pizza), c1))), 1)
        asserte(len(list(triples((bob, Any, michel), c1))), 1)

        # unbound predicates without context, same results!
        asserte(len(list(triples((michel, Any, cheese)))), 1)
        asserte(len(list(triples((tarek, Any, cheese)))), 1)
        asserte(len(list(triples((bob, Any, pizza)))), 1)
        asserte(len(list(triples((bob, Any, michel)))), 1)

        # unbound subject, objects with context
        asserte(len(list(triples((Any, hates, Any), c1))), 2)
        asserte(len(list(triples((Any, likes, Any), c1))), 5)

        # unbound subject, objects without context, same results!
        asserte(len(list(triples((Any, hates, Any)))), 2)
        asserte(len(list(triples((Any, likes, Any)))), 5)

        # unbound predicates, objects with context
        asserte(len(list(triples((michel, Any, Any), c1))), 2)
        asserte(len(list(triples((bob, Any, Any), c1))), 3)
        asserte(len(list(triples((tarek, Any, Any), c1))), 2)

        # unbound predicates, objects without context, same results!
        asserte(len(list(triples((michel, Any, Any)))), 2)
        asserte(len(list(triples((bob, Any, Any)))), 3)
        asserte(len(list(triples((tarek, Any, Any)))), 2)

        # unbound subjects, predicates with context
        asserte(len(list(triples((Any, Any, pizza), c1))), 3)
        asserte(len(list(triples((Any, Any, cheese), c1))), 3)
        asserte(len(list(triples((Any, Any, michel), c1))), 1)

        # unbound subjects, predicates without context, same results!
        asserte(len(list(triples((Any, Any, pizza)))), 3)
        asserte(len(list(triples((Any, Any, cheese)))), 3)
        asserte(len(list(triples((Any, Any, michel)))), 1)

        # all unbound with context
        asserte(len(list(triples((Any, Any, Any), c1))), 7)
        # all unbound without context, same result!
        asserte(len(list(triples((Any, Any, Any)))), 7)

        for c in [graph, graph.get_context(c1)]:
            # unbound subjects
            c = graph.get_context(c1)
            asserte(set(c.subjects(likes, pizza)), set((michel, tarek)))
            asserte(set(c.subjects(hates, pizza)), set((bob,)))
            asserte(set(c.subjects(likes, cheese)), set([tarek, bob, michel]))
            asserte(set(c.subjects(hates, cheese)), set())

            # unbound objects
            asserte(set(c.objects(michel, likes)), set([cheese, pizza]))
            asserte(set(c.objects(tarek, likes)), set([cheese, pizza]))
            asserte(set(c.objects(bob, hates)), set([michel, pizza]))
            asserte(set(c.objects(bob, likes)), set([cheese]))

            # unbound predicates
            asserte(set(c.predicates(michel, cheese)), set([likes]))
            asserte(set(c.predicates(tarek, cheese)), set([likes]))
            asserte(set(c.predicates(bob, pizza)), set([hates]))
            asserte(set(c.predicates(bob, michel)), set([hates]))

            asserte(set(c.subject_objects(hates)), set([(bob, pizza), (bob, michel)]))
            asserte(set(c.subject_objects(likes)), set([(tarek, cheese), (michel, cheese), (michel, pizza), (bob, cheese), (tarek, pizza)]))

            asserte(set(c.predicate_objects(michel)), set([(likes, cheese), (likes, pizza)]))
            asserte(set(c.predicate_objects(bob)), set([(likes, cheese), (hates, pizza), (hates, michel)]))
            asserte(set(c.predicate_objects(tarek)), set([(likes, cheese), (likes, pizza)]))

            asserte(set(c.subject_predicates(pizza)), set([(bob, hates), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(cheese)), set([(bob, likes), (tarek, likes), (michel, likes)]))
            asserte(set(c.subject_predicates(michel)), set([(bob, hates)]))

            asserte(set(c.triples((None, None, None))), set([(bob, hates, michel), (bob, likes, cheese), (tarek, likes, pizza), (michel, likes, pizza), (michel, likes, cheese), (bob, hates, pizza), (tarek, likes, cheese)]))

        # remove stuff and make sure the graph is empty again
        self.removeStuff()
        asserte(len(list(triples((Any, Any, Any), c1))), 0)
        asserte(len(list(triples((Any, Any, Any)))), 0)

class IOMemoryContextTestCase(ContextTestCase):
    backend = "IOMemory"

try:
    from rdflib.store.Sleepycat import Sleepycat
    class SleepycatStoreTestCase(ContextTestCase):
        backend = "Sleepycat"
except ImportError, e:
    print "Can not test Sleepycat store:", e

try:
    import persistent
    # If we can import persistent then test ZODB backend
    class ZODBContextTestCase(ContextTestCase):
        backend = "ZODB"
except ImportError:
    pass

try:
    import backends.Sleepycat
except ImportError:
    del ContextTestCase

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>#!/usr/bin/python3
# PYTHON_ARGCOMPLETE_OK

# Copyright 2019-2020 Fiona Klute
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import asyncio
import contextlib
import itertools
import os
import subprocess
import sys
import tempfile
import yaml
from pathlib import Path
from unittest import SkipTest

import mgstest.hooks
import mgstest.valgrind
from mgstest import lockfile, TestExpectationFailed
from mgstest.services import ApacheService, TestService
from mgstest.tests import run_test_conf

def find_testdir(number, dir):
    """Find the configuration directory for a test based on its
    number. The given directory must contain exactly one directory
    with a name matching "NUMBER_*", otherwise a LookupError is
    raised.

    """
    found = None
    for entry in dir.iterdir():
        if entry.is_dir():
            num = int(entry.name.split('_', maxsplit=1)[0])
            if number == num:
                if found:
                    # duplicate numbers are an error
                    raise LookupError('Multiple directories found for '
                                      f'test number {number}: '
                                      f'{found.name} and {entry.name}')
                else:
                    found = entry
    if found is None:
        raise LookupError('No test directory found for test number '
                          f'{number}!')
    return found, found.name

def temp_logfile():
    return tempfile.SpooledTemporaryFile(max_size=4096, mode='w+',
                                         prefix='mod_gnutls', suffix=".log")

async def check_ocsp_responder():
    # Check if OCSP responder works
    issuer_cert = 'authority/x509.pem'
    check_cert = 'authority/server/x509.pem'
    proc = await asyncio.create_subprocess_exec(
        'ocsptool', '--ask', '--nonce',
        '--load-issuer', issuer_cert, '--load-cert', check_cert)
    return (await proc.wait()) == 0

async def check_msva():
    # Check if MSVA is up
    cert_file = 'authority/client/x509.pem'
    uid_file = 'authority/client/uid'
    with open(uid_file, 'r') as file:
        uid = file.read().strip()
    with open(cert_file, 'r') as cert:
        proc = await asyncio.create_subprocess_exec(
            'msva-query-agent', 'https', uid, 'x509pem', 'client',
            stdin=cert)
        return (await proc.wait()) == 0

async def main(args):
    # The Automake environment always provides srcdir, the default is
    # for manual use.
    srcdir = Path(os.environ.get('srcdir', '.')).resolve()
    # ensure environment srcdir is absolute
    os.environ['srcdir'] = str(srcdir)

    # Find the configuration directory for the test in
    # ${srcdir}/tests/, based on the test number.
    testdir, testname = find_testdir(args.test_number, srcdir / 'tests')
    print(f'Found test {testname}, test dir is {testdir}')
    os.environ['TEST_NAME'] = testname

    # Load test config
    try:
        with open(testdir / 'test.yaml', 'r') as conf_file:
            test_conf = yaml.load(conf_file, Loader=yaml.Loader)
    except FileNotFoundError:
        test_conf = None

    # Load test case hooks (if any)
    plugin = mgstest.hooks.load_hooks_plugin(testdir / 'hooks.py')

    # PID file name varies depending on whether we're using
    # namespaces.
    #
    # TODO: Check if having the different names is really necessary.
    pidaffix = ''
    if 'USE_TEST_NAMESPACE' in os.environ:
        pidaffix = f'-{testname}'

    valgrind_log = None
    if args.valgrind:
        valgrind_log = Path('logs', f'valgrind-{testname}.log')

    # Define the available services
    apache = ApacheService(config=testdir / 'apache.conf',
                           pidfile=f'apache2{pidaffix}.pid',
                           valgrind_log=valgrind_log,
                           valgrind_suppress=args.valgrind_suppressions)
    backend = ApacheService(config=testdir / 'backend.conf',
                            pidfile=f'backend{pidaffix}.pid')
    ocsp = ApacheService(config=testdir / 'ocsp.conf',
                         pidfile=f'ocsp{pidaffix}.pid',
                         check=check_ocsp_responder)
    msva = TestService(start=['monkeysphere-validation-agent'],
                       env={'GNUPGHOME': 'msva.gnupghome',
                            'MSVA_KEYSERVER_POLICY': 'never'},
                       condition=lambda: 'USE_MSVA' in os.environ,
                       check=check_msva)

    # background services: must be ready before the main apache
    # instance is started
    bg_services = [backend, ocsp, msva]

    # If VERBOSE is enabled, log the HTTPD build configuration
    if 'VERBOSE' in os.environ:
        apache2 = os.environ.get('APACHE2', 'apache2')
        subprocess.run([apache2, '-f', f'{srcdir}/base_apache.conf', '-V'],
                       check=True)

    # This hook may modify the environment as needed for the test.
    cleanup_callback = None
    try:
        if plugin.prepare_env:
            cleanup_callback = plugin.prepare_env()
    except SkipTest as skip:
        print(f'Skipping: {skip!s}')
        sys.exit(77)

    if 'USE_MSVA' in os.environ:
        os.environ['MONKEYSPHERE_VALIDATION_AGENT_SOCKET'] = \
            f'http://127.0.0.1:{os.environ["MSVA_PORT"]}'

    async with contextlib.AsyncExitStack() as service_stack:
        if cleanup_callback:
            service_stack.callback(cleanup_callback)
        service_stack.enter_context(
            lockfile('test.lock', nolock='MGS_NETNS_ACTIVE' in os.environ))

        # TEST_SERVICE_MAX_WAIT is in milliseconds
        wait_timeout = \
            int(os.environ.get('TEST_SERVICE_MAX_WAIT', 10000)) / 1000
        await asyncio.wait(
            {asyncio.create_task(
                service_stack.enter_async_context(
                    s.run(ready_timeout=wait_timeout)))
             for s in bg_services})

        # special case: expected to fail in a few cases
        await service_stack.enter_async_context(apache.run())
        failed = await apache.wait_ready()
        if (testdir / 'fail.server').is_file():
            if failed:
                print('Apache server failed to start as expected',
                      file=sys.stderr)
            else:
                raise TestExpectationFailed(
                    'Server start did not fail as expected!')

        # Set TEST_TARGET for the request. Might be replaced with a
        # parameter later.
        if 'TARGET_IP' in os.environ:
            os.environ['TEST_TARGET'] = os.environ['TARGET_IP']
        else:
            os.environ['TEST_TARGET'] = os.environ['TEST_HOST']

        # Run the test connections
        if plugin.run_connection:
            plugin.run_connection(testname,
                                  conn_log=args.log_connection,
                                  response_log=args.log_responses)
        else:
            run_test_conf(test_conf,
                          float(os.environ.get('TEST_QUERY_TIMEOUT', 5.0)),
                          conn_log=args.log_connection,
                          response_log=args.log_responses)

        await asyncio.wait(
            {asyncio.create_task(s.stop())
             for s in itertools.chain([apache], bg_services)})

    # run extra checks the test's hooks.py might define
    if plugin.post_check:
        args.log_connection.seek(0)
        args.log_responses.seek(0)
        plugin.post_check(conn_log=args.log_connection,
                          response_log=args.log_responses)

    if valgrind_log:
        with open(valgrind_log) as log:
            errors = mgstest.valgrind.error_summary(log)
            print(f'Valgrind summary: {errors[0]} errors, '
                  f'{errors[1]} suppressed')
            if errors[0] > 0:
                sys.exit(ord('V'))

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(
        description='Run a mod_gnutls server test')
    parser.add_argument('--test-number', type=int,
                        required=True, help='load YAML test configuration')
    parser.add_argument('--log-connection', type=argparse.FileType('w+'),
                        default=temp_logfile(),
                        help='write connection log to this file')
    parser.add_argument('--log-responses', type=argparse.FileType('w+'),
                        default=temp_logfile(),
                        help='write HTTP responses to this file')
    parser.add_argument('--valgrind', action='store_true',
                        help='run primary Apache instance with Valgrind')
    parser.add_argument('--valgrind-suppressions', action='append',
                        default=[],
                        help='use Valgrind suppressions file')

    # enable bash completion if argcomplete is available
    try:
        import argcomplete
        argcomplete.autocomplete(parser)
    except ImportError:
        pass

    args = parser.parse_args()

    with contextlib.ExitStack() as stack:
        stack.enter_context(contextlib.closing(args.log_connection))
        stack.enter_context(contextlib.closing(args.log_responses))
        asyncio.run(main(args))
<commit_msg>Use asyncio.gather() when starting services to raise exceptions<commit_after>#!/usr/bin/python3
# PYTHON_ARGCOMPLETE_OK

# Copyright 2019-2020 Fiona Klute
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import asyncio
import contextlib
import itertools
import os
import subprocess
import sys
import tempfile
import yaml
from pathlib import Path
from unittest import SkipTest

import mgstest.hooks
import mgstest.valgrind
from mgstest import lockfile, TestExpectationFailed
from mgstest.services import ApacheService, TestService
from mgstest.tests import run_test_conf

def find_testdir(number, dir):
    """Find the configuration directory for a test based on its
    number. The given directory must contain exactly one directory
    with a name matching "NUMBER_*", otherwise a LookupError is
    raised.

    """
    found = None
    for entry in dir.iterdir():
        if entry.is_dir():
            num = int(entry.name.split('_', maxsplit=1)[0])
            if number == num:
                if found:
                    # duplicate numbers are an error
                    raise LookupError('Multiple directories found for '
                                      f'test number {number}: '
                                      f'{found.name} and {entry.name}')
                else:
                    found = entry
    if found is None:
        raise LookupError('No test directory found for test number '
                          f'{number}!')
    return found, found.name

def temp_logfile():
    return tempfile.SpooledTemporaryFile(max_size=4096, mode='w+',
                                         prefix='mod_gnutls', suffix=".log")

async def check_ocsp_responder():
    # Check if OCSP responder works
    issuer_cert = 'authority/x509.pem'
    check_cert = 'authority/server/x509.pem'
    proc = await asyncio.create_subprocess_exec(
        'ocsptool', '--ask', '--nonce',
        '--load-issuer', issuer_cert, '--load-cert', check_cert)
    return (await proc.wait()) == 0

async def check_msva():
    # Check if MSVA is up
    cert_file = 'authority/client/x509.pem'
    uid_file = 'authority/client/uid'
    with open(uid_file, 'r') as file:
        uid = file.read().strip()
    with open(cert_file, 'r') as cert:
        proc = await asyncio.create_subprocess_exec(
            'msva-query-agent', 'https', uid, 'x509pem', 'client',
            stdin=cert)
        return (await proc.wait()) == 0

async def main(args):
    # The Automake environment always provides srcdir, the default is
    # for manual use.
    srcdir = Path(os.environ.get('srcdir', '.')).resolve()
    # ensure environment srcdir is absolute
    os.environ['srcdir'] = str(srcdir)

    # Find the configuration directory for the test in
    # ${srcdir}/tests/, based on the test number.
    testdir, testname = find_testdir(args.test_number, srcdir / 'tests')
    print(f'Found test {testname}, test dir is {testdir}')
    os.environ['TEST_NAME'] = testname

    # Load test config
    try:
        with open(testdir / 'test.yaml', 'r') as conf_file:
            test_conf = yaml.load(conf_file, Loader=yaml.Loader)
    except FileNotFoundError:
        test_conf = None

    # Load test case hooks (if any)
    plugin = mgstest.hooks.load_hooks_plugin(testdir / 'hooks.py')

    # PID file name varies depending on whether we're using
    # namespaces.
    #
    # TODO: Check if having the different names is really necessary.
    pidaffix = ''
    if 'USE_TEST_NAMESPACE' in os.environ:
        pidaffix = f'-{testname}'

    valgrind_log = None
    if args.valgrind:
        valgrind_log = Path('logs', f'valgrind-{testname}.log')

    # Define the available services
    apache = ApacheService(config=testdir / 'apache.conf',
                           pidfile=f'apache2{pidaffix}.pid',
                           valgrind_log=valgrind_log,
                           valgrind_suppress=args.valgrind_suppressions)
    backend = ApacheService(config=testdir / 'backend.conf',
                            pidfile=f'backend{pidaffix}.pid')
    ocsp = ApacheService(config=testdir / 'ocsp.conf',
                         pidfile=f'ocsp{pidaffix}.pid',
                         check=check_ocsp_responder)
    msva = TestService(start=['monkeysphere-validation-agent'],
                       env={'GNUPGHOME': 'msva.gnupghome',
                            'MSVA_KEYSERVER_POLICY': 'never'},
                       condition=lambda: 'USE_MSVA' in os.environ,
                       check=check_msva)

    # background services: must be ready before the main apache
    # instance is started
    bg_services = [backend, ocsp, msva]

    # If VERBOSE is enabled, log the HTTPD build configuration
    if 'VERBOSE' in os.environ:
        apache2 = os.environ.get('APACHE2', 'apache2')
        subprocess.run([apache2, '-f', f'{srcdir}/base_apache.conf', '-V'],
                       check=True)

    # This hook may modify the environment as needed for the test.
    cleanup_callback = None
    try:
        if plugin.prepare_env:
            cleanup_callback = plugin.prepare_env()
    except SkipTest as skip:
        print(f'Skipping: {skip!s}')
        sys.exit(77)

    if 'USE_MSVA' in os.environ:
        os.environ['MONKEYSPHERE_VALIDATION_AGENT_SOCKET'] = \
            f'http://127.0.0.1:{os.environ["MSVA_PORT"]}'

    async with contextlib.AsyncExitStack() as service_stack:
        if cleanup_callback:
            service_stack.callback(cleanup_callback)
        service_stack.enter_context(
            lockfile('test.lock', nolock='MGS_NETNS_ACTIVE' in os.environ))

        # TEST_SERVICE_MAX_WAIT is in milliseconds
        wait_timeout = \
            int(os.environ.get('TEST_SERVICE_MAX_WAIT', 10000)) / 1000
        await asyncio.gather(*(
            asyncio.create_task(
                service_stack.enter_async_context(
                    s.run(ready_timeout=wait_timeout)))
            for s in bg_services))

        # special case: expected to fail in a few cases
        await service_stack.enter_async_context(apache.run())
        failed = await apache.wait_ready()
        if (testdir / 'fail.server').is_file():
            if failed:
                print('Apache server failed to start as expected',
                      file=sys.stderr)
            else:
                raise TestExpectationFailed(
                    'Server start did not fail as expected!')

        # Set TEST_TARGET for the request. Might be replaced with a
        # parameter later.
        if 'TARGET_IP' in os.environ:
            os.environ['TEST_TARGET'] = os.environ['TARGET_IP']
        else:
            os.environ['TEST_TARGET'] = os.environ['TEST_HOST']

        # Run the test connections
        if plugin.run_connection:
            plugin.run_connection(testname,
                                  conn_log=args.log_connection,
                                  response_log=args.log_responses)
        else:
            run_test_conf(test_conf,
                          float(os.environ.get('TEST_QUERY_TIMEOUT', 5.0)),
                          conn_log=args.log_connection,
                          response_log=args.log_responses)

        await asyncio.wait(
            {asyncio.create_task(s.stop())
             for s in itertools.chain([apache], bg_services)})

    # run extra checks the test's hooks.py might define
    if plugin.post_check:
        args.log_connection.seek(0)
        args.log_responses.seek(0)
        plugin.post_check(conn_log=args.log_connection,
                          response_log=args.log_responses)

    if valgrind_log:
        with open(valgrind_log) as log:
            errors = mgstest.valgrind.error_summary(log)
            print(f'Valgrind summary: {errors[0]} errors, '
                  f'{errors[1]} suppressed')
            if errors[0] > 0:
                sys.exit(ord('V'))

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(
        description='Run a mod_gnutls server test')
    parser.add_argument('--test-number', type=int,
                        required=True, help='load YAML test configuration')
    parser.add_argument('--log-connection', type=argparse.FileType('w+'),
                        default=temp_logfile(),
                        help='write connection log to this file')
    parser.add_argument('--log-responses', type=argparse.FileType('w+'),
                        default=temp_logfile(),
                        help='write HTTP responses to this file')
    parser.add_argument('--valgrind', action='store_true',
                        help='run primary Apache instance with Valgrind')
    parser.add_argument('--valgrind-suppressions', action='append',
                        default=[],
                        help='use Valgrind suppressions file')

    # enable bash completion if argcomplete is available
    try:
        import argcomplete
        argcomplete.autocomplete(parser)
    except ImportError:
        pass

    args = parser.parse_args()

    with contextlib.ExitStack() as stack:
        stack.enter_context(contextlib.closing(args.log_connection))
        stack.enter_context(contextlib.closing(args.log_responses))
        asyncio.run(main(args))
<|endoftext|>


<commit_before>import os
import json

from google.appengine.api import memcache
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

from base_controller import BaseHandler, CacheableHandler

from models.event import Event
from models.team import Team
from models.sitevar import Sitevar
import tba_config

class TypeaheadHandler(CacheableHandler):
    """
    Currently just returns a list of all teams and events
    Needs to be optimized at some point.
    Tried a trie but the datastructure was too big to
    fit into memcache efficiently
    """
    def __init__(self, *args, **kw):
        super(TypeaheadHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24
        self._cache_key = "typeahead_entries"
        self._cache_version = 1

    def get(self):
        self.response.headers['Cache-Control'] = "public, max-age=%d" % (6*60*60)
        self.response.headers['Pragma'] = 'Public'
        self.response.headers.add_header('content-type', 'application/json', charset='utf-8')
        super(TypeaheadHandler, self).get()

    def _render(self):
        @ndb.tasklet
        def get_events_async():
            event_keys = yield Event.query().order(-Event.year).order(Event.name).fetch_async(keys_only=True)
            events = yield ndb.get_multi_async(event_keys)
            raise ndb.Return(events)

        @ndb.tasklet
        def get_teams_async():
            team_keys = yield Team.query().order(Team.team_number).fetch_async(keys_only=True)
            teams = yield ndb.get_multi_async(team_keys)
            raise ndb.Return(teams)

        @ndb.toplevel
        def get_events_and_teams():
            events, teams = yield get_events_async(), get_teams_async()
            raise ndb.Return((events, teams))

        events, teams = get_events_and_teams()

        results = []
        for event in events:
            results.append({'id': event.key_name, 'name': '%s %s [%s]' % (event.year, event.name, event.event_short.upper())})
        for team in teams:
            if not team.nickname:
                nickname = "Team %s" % team.team_number
            else:
                nickname = team.nickname
            results.append({'id': team.team_number, 'name': '%s | %s' % (team.team_number, nickname)})

        return json.dumps(results)

class WebcastHandler(CacheableHandler):
    """
    Returns the HTML necessary to generate the webcast embed for a given event
    """
    def __init__(self, *args, **kw):
        super(WebcastHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24
        self._cache_key = "webcast_{}_{}" # (event_key)
        self._cache_version = 1

    def get(self, event_key, webcast_number):
        webcast_number = int(webcast_number) - 1
        self._cache_key = self._cache_key.format(event_key, webcast_number)

        self.response.headers['Cache-Control'] = "public, max-age=%d" % (5*60)
        self.response.headers['Pragma'] = 'Public'
        self.response.headers.add_header('content-type', 'application/json', charset='utf-8')

        super(WebcastHandler, self).get(event_key, webcast_number)

    def _render(self, event_key, webcast_number):
        event = Event.get_by_id(event_key)
        output = {}
        if event and event.webcast:
            webcast = event.webcast[webcast_number]
            if 'type' in webcast and 'channel' in webcast:
                output['player'] = self._renderPlayer(webcast)
        else:
            special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts')
            special_webcasts = special_webcasts_future.get_result()
            if special_webcasts:
                special_webcasts = special_webcasts.contents
            else:
                special_webcasts = {}
            if event_key in special_webcasts:
                webcast = special_webcasts[event_key]
                if 'type' in webcast and 'channel' in webcast:
                    output['player'] = self._renderPlayer(webcast)

        return json.dumps(output)

    def _renderPlayer(self, webcast):
        webcast_type = webcast['type']
        template_values = {'webcast': webcast}

        path = os.path.join(os.path.dirname(__file__), '../templates/webcast/' + webcast_type + '.html')
        return template.render(path, template_values)

    def memcacheFlush(self, event_key):
        keys = [self.cache_key.format(event_key, n) for n in range(10)]
        memcache.delete_multi(keys)
        return keys

<commit_msg>Disable typeahead for now.<commit_after>import os
import json

from google.appengine.api import memcache
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

from base_controller import BaseHandler, CacheableHandler

from models.event import Event
from models.team import Team
from models.sitevar import Sitevar
import tba_config

class TypeaheadHandler(CacheableHandler):
    """
    Currently just returns a list of all teams and events
    Needs to be optimized at some point.
    Tried a trie but the datastructure was too big to
    fit into memcache efficiently
    """
    def __init__(self, *args, **kw):
        super(TypeaheadHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24
        self._cache_key = "typeahead_entries"
        self._cache_version = 1

    def get(self):
        self.response.headers['Cache-Control'] = "public, max-age=%d" % (6*60*60)
        self.response.headers['Pragma'] = 'Public'
        self.response.headers.add_header('content-type', 'application/json', charset='utf-8')
        super(TypeaheadHandler, self).get()

    def _render(self):
        # The typeahead times out a lot and causes instances to go over memory limits
        # Disabled until we fix it.
        # todo: make this work again -gregmarra 20130511
        return "{}"

        # \/ \/ never runs \/ \/

        @ndb.tasklet
        def get_events_async():
            event_keys = yield Event.query().order(-Event.year).order(Event.name).fetch_async(keys_only=True)
            events = yield ndb.get_multi_async(event_keys)
            raise ndb.Return(events)

        @ndb.tasklet
        def get_teams_async():
            team_keys = yield Team.query().order(Team.team_number).fetch_async(keys_only=True)
            teams = yield ndb.get_multi_async(team_keys)
            raise ndb.Return(teams)

        @ndb.toplevel
        def get_events_and_teams():
            events, teams = yield get_events_async(), get_teams_async()
            raise ndb.Return((events, teams))

        events, teams = get_events_and_teams()

        results = []
        for event in events:
            results.append({'id': event.key_name, 'name': '%s %s [%s]' % (event.year, event.name, event.event_short.upper())})
        for team in teams:
            if not team.nickname:
                nickname = "Team %s" % team.team_number
            else:
                nickname = team.nickname
            results.append({'id': team.team_number, 'name': '%s | %s' % (team.team_number, nickname)})

        return json.dumps(results)

class WebcastHandler(CacheableHandler):
    """
    Returns the HTML necessary to generate the webcast embed for a given event
    """
    def __init__(self, *args, **kw):
        super(WebcastHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24
        self._cache_key = "webcast_{}_{}" # (event_key)
        self._cache_version = 1

    def get(self, event_key, webcast_number):
        webcast_number = int(webcast_number) - 1
        self._cache_key = self._cache_key.format(event_key, webcast_number)

        self.response.headers['Cache-Control'] = "public, max-age=%d" % (5*60)
        self.response.headers['Pragma'] = 'Public'
        self.response.headers.add_header('content-type', 'application/json', charset='utf-8')

        super(WebcastHandler, self).get(event_key, webcast_number)

    def _render(self, event_key, webcast_number):
        event = Event.get_by_id(event_key)
        output = {}
        if event and event.webcast:
            webcast = event.webcast[webcast_number]
            if 'type' in webcast and 'channel' in webcast:
                output['player'] = self._renderPlayer(webcast)
        else:
            special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts')
            special_webcasts = special_webcasts_future.get_result()
            if special_webcasts:
                special_webcasts = special_webcasts.contents
            else:
                special_webcasts = {}
            if event_key in special_webcasts:
                webcast = special_webcasts[event_key]
                if 'type' in webcast and 'channel' in webcast:
                    output['player'] = self._renderPlayer(webcast)

        return json.dumps(output)

    def _renderPlayer(self, webcast):
        webcast_type = webcast['type']
        template_values = {'webcast': webcast}

        path = os.path.join(os.path.dirname(__file__), '../templates/webcast/' + webcast_type + '.html')
        return template.render(path, template_values)

    def memcacheFlush(self, event_key):
        keys = [self.cache_key.format(event_key, n) for n in range(10)]
        memcache.delete_multi(keys)
        return keys

<|endoftext|>


<commit_before>#!/usr/bin/python2.4
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License')
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Reflow Test Definitions."""

__author__ = 'elsigh@google.com (Lindsey Simon)'

from controllers import test_set_base

_CATEGORY = 'reflow'

class ReflowTest(object):
  TESTS_URL_PATH = '/%s/test' % _CATEGORY
  def __init__(self, key, name, doc):
    self.key = key
    self.name = name
    self.url = '%s?t=%s' % (self.TESTS_URL_PATH, key)
    self.score_type = 'custom'
    self.doc = doc
    self.min_value, self.max_value = 0, 60000

  def GetScoreAndDisplayValue(self, median):
    """Returns a tuple with display text for the cell as well as a 1-100 value.
    i.e. ('1X', 95)
    Args:
      median: The test median.
    Returns:
      A tuple of display, score
    """
    if not median:
      return 0, '0.0'
    time = round(float(median) / 1000.0, 1)
    abc = (1, 2, 3, 4)
    if self.key is 'testDisplay':
      abc = (.3, .5, 1, 1.5)
    elif self.key is 'testVisibility':
      abc = (.4, .7, 1.1, 1.6)
    elif self.key is 'testNonMatchingClass':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testFourClassReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testFourScriptReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testTwoScriptReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testPaddingPx':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testPaddingLeftPx':
      abc = (.6, 1, 1.5, 2)
    elif self.key is 'testFontSizeEm':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testWidthPercent':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testBackground':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testOverflowHidden':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testSelectorMatchTime':
      abc = (.6, 1, 1.5, 2)
    elif self.key is 'testGetOffsetHeight':
      abc = (0, .1, .2, .3)
    score = self._GetScore(time, abc)
    #logging.info('CustomTestsFunction w/ %s, %s' % (test, median))
    #logging.info('TEST!! %s, %s, %s' % (test, time, score))
    return score, str(time)

  @staticmethod
  def _GetScore(time, abc):
    """Computes an A/B/C/D/F-like score out of 100.

    Args:
      time: a float to score
      abc: (a_cutoff, b_cutoff, c_cutoff, d_cutoff)
    """
    # Make the score proportional to where the time falls in the abc ranges.
    if time <= abc[0]:
      score = int(round(90.0 + (abc[0] - time) / abc[0] * 10))             # A
    elif abc[0] < time <= abc[1]:
      score = int(round(80.0 + (abc[1] - time) / (abc[1] - abc[0]) * 10))  # B
    elif abc[1] < time <= abc[2]:
      score = int(round(70.0 + (abc[2] - time) / (abc[2] - abc[1]) * 10))  # C
    elif abc[2] < time <= abc[3]:
      score = int(round(60.0 + (abc[3] - time) / (abc[3] - abc[2]) * 10))  # D
    else:
      score = 10  # F
    return score

_TESTS = (
  # key, name, doc
  ReflowTest('testDisplay', 'Display Block',
    '''This test takes an element and sets its
    style.display="none". According to our friends at Mozilla this has
    the effect of taking an element out of the browser's "render tree" -
    the in-memory representation of all of results of
    geometry/positioning calculations for that particular
    element. Setting an element to display="none" has the additional
    effect of removing all of an elements children from the render tree
    as well. Next, the test resets the element's style.display="", which
    sets the element's display back to its original value. Our thinking
    was that this operation ought to approximate the cost of reflowing
    an element on a particular page since the browser would have to
    recalculate all the positions and sizes for every child within the
    element as well as make any changes to the overall document that
    this change would cause to parents and ancestors. This was
    originally the only test that the Reflow Timer performed, but as you
    can see from the results, we discovered that not all render engines
    work like Gecko's and so we began adding more tests.'''),
  ReflowTest('testVisibility', 'Visiblility None',
    '''Much like the display test above, this test sets an element's
    style.visibility="hidden" and then resets it back to its default,
    which is visible. Visually this has the same effect as setting
    display="none", however this change should be significantly less
    costly for the browser to perform as it should not need be purging
    the element from the render tree and recalculating
    geometries.'''),
  ReflowTest('testNonMatchingClass', 'Non Matching by Class',
    '''This test adds a class name to an element, and more specifically a
    class name which is not present in the document's CSS object
    model.'''),
  ReflowTest('testFourClassReflows', 'Four Reflows by Class',
    '''This test adds a class name to an element that will match a
    previously added CSS declaration added to the CSSOM. This
    declaration is set with four property value pairs which should in
    and of themselves be capable of causing a 1x reflow time. For
    instance, "font-size: 20px; line-height: 10px; padding-left: 10px;
    margin-top: 7px;". This test aims to test whether reflow
    operations occur in a single queue flush or if they are performed
    one at a time when these changes are made via a CSS
    classname. This test is a sort of opposite to the Four Reflows By
    Script.'''),
  ReflowTest('testFourScriptReflows', 'Four Reflows by Script',
    '''Like the Four Reflows By Class test, but instead this test has
    four lines of Javascript, each of which alters the style object
    with a property/value that by itself could cause a 1x reflow
    time.'''),
  ReflowTest('testTwoScriptReflows', 'Two Reflows by Script',
    '''Like the Four Reflows By Script test, except with only two lines
    of Javascript.'''),
  ReflowTest('testPaddingPx', 'Padding px',
    '''This test sets style.padding="FOOpx", aka padding on all sides of
    the box model.'''),
  ReflowTest('testPaddingLeftPx', 'Padding Left px',
    '''This test sets style.paddingLeft="FOOpx", aka padding on only the
    left side of the box.'''),
  ReflowTest('testFontSizeEm', 'Font Size em',
    '''This test changes an element's style.fontSize to an em-based
    value.'''),
  ReflowTest('testWidthPercent', 'Width %',
    '''This test sets an element's style.width="FOO%"'''),
  ReflowTest('testBackground', 'Background Color',
    '''This test sets an element's style.background="#FOO", aka a
    hexadecimal color.'''),
  ReflowTest('testOverflowHidden', 'Overflow Hidden',
    '''This test sets an element's style.overflow="hidden" and then back
    again, timing the cost of an element returning to the default
    overflow which is "visible"'''),
  ReflowTest('testSelectorMatchTime', 'Selector Match Time',
    '''In the world of reflow, one of the possible operations that can
    eat up time occurs when an element gets a new class name and the
    render engine has to look through the CSSOM to see if this element
    now matches some CSS declaration. The goal of this test is to try
    to cause this match operation without causing any change to the
    render tree. The test is much like the Non-Matching Class test,
    except that instead of flushing the render queue before this test,
    we try to simply flush any style computations. The way it works is
    to make a CSS rule that can get activated by virtue of an
    attribute selector but which will never cause reflow because it
    will never match any element in the render tree. For instance
    "body[bogusattr='bogusval'] html {color:pink}". We note that this
    test seems not to work in engines other than Gecko at this
    time.'''),
  ReflowTest('testGetOffsetHeight', 'Do Nothing / OffsetHeight',
    '''This test does nothing other than the request for offsetHeight
    after already having done so. Theoretically, this test is
    something like a control for our test and should have a 0 or very
    low time.'''),
)

TEST_SET = test_set_base.TestSet(
    category=_CATEGORY,
    category_name='Reflow',
    tests=_TESTS,
    subnav={
      'Test': '/%s/test' % _CATEGORY,
      'About': '/%s/about' % _CATEGORY
    },
    home_intro='''Reflow in a web browser refers to the process whereby the render engine calculates positions and geometries of elements in the document for purpose of drawing, or re-drawing, the visual presentation. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow times and also to understand the effects of various document properties (DOM nesting, CSS specificity, types of style changes) on reflow. Read more about reflow and these tests.''',
    default_params=[
    'nested_anchors', 'num_elements=400', 'num_nest=4',
    'css_selector=%23g-content%20*', 'num_css_rules=1000',
    'css_text=border%3A%201px%20solid%20%230C0%3B%20padding%3A%208px%3B'],
)
<commit_msg>fixed 0 testGetOffsetHeight case<commit_after>#!/usr/bin/python2.4
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License')
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Reflow Test Definitions."""

__author__ = 'elsigh@google.com (Lindsey Simon)'

from controllers import test_set_base

_CATEGORY = 'reflow'

class ReflowTest(object):
  TESTS_URL_PATH = '/%s/test' % _CATEGORY
  def __init__(self, key, name, doc):
    self.key = key
    self.name = name
    self.url = '%s?t=%s' % (self.TESTS_URL_PATH, key)
    self.score_type = 'custom'
    self.doc = doc
    self.min_value, self.max_value = 0, 60000

  def GetScoreAndDisplayValue(self, median):
    """Returns a tuple with display text for the cell as well as a 1-100 value.
    i.e. ('1X', 95)
    Args:
      median: The test median.
    Returns:
      A tuple of display, score
    """
    if not median:
      return 0, '0.0'
    time = round(float(median) / 1000.0, 1)
    abc = (1, 2, 3, 4)
    if self.key is 'testDisplay':
      abc = (.3, .5, 1, 1.5)
    elif self.key is 'testVisibility':
      abc = (.4, .7, 1.1, 1.6)
    elif self.key is 'testNonMatchingClass':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testFourClassReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testFourScriptReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testTwoScriptReflows':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testPaddingPx':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testPaddingLeftPx':
      abc = (.6, 1, 1.5, 2)
    elif self.key is 'testFontSizeEm':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testWidthPercent':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testBackground':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testOverflowHidden':
      abc = (.6, 1, 2, 3)
    elif self.key is 'testSelectorMatchTime':
      abc = (.6, 1, 1.5, 2)
    elif self.key is 'testGetOffsetHeight':
      abc = (.1, .2, .3, .4)
    score = self._GetScore(time, abc)
    #logging.info('CustomTestsFunction w/ %s, %s' % (test, median))
    #logging.info('TEST!! %s, %s, %s' % (test, time, score))
    return score, str(time)

  @staticmethod
  def _GetScore(time, abc):
    """Computes an A/B/C/D/F-like score out of 100.

    Args:
      time: a float to score
      abc: (a_cutoff, b_cutoff, c_cutoff, d_cutoff)
    """
    # Make the score proportional to where the time falls in the abc ranges.
    if time <= abc[0]:
      score = int(round(90.0 + (abc[0] - time) / abc[0] * 10))             # A
    elif abc[0] < time <= abc[1]:
      score = int(round(80.0 + (abc[1] - time) / (abc[1] - abc[0]) * 10))  # B
    elif abc[1] < time <= abc[2]:
      score = int(round(70.0 + (abc[2] - time) / (abc[2] - abc[1]) * 10))  # C
    elif abc[2] < time <= abc[3]:
      score = int(round(60.0 + (abc[3] - time) / (abc[3] - abc[2]) * 10))  # D
    else:
      score = 10  # F
    return score

_TESTS = (
  # key, name, doc
  ReflowTest('testDisplay', 'Display Block',
    '''This test takes an element and sets its
    style.display="none". According to our friends at Mozilla this has
    the effect of taking an element out of the browser's "render tree" -
    the in-memory representation of all of results of
    geometry/positioning calculations for that particular
    element. Setting an element to display="none" has the additional
    effect of removing all of an elements children from the render tree
    as well. Next, the test resets the element's style.display="", which
    sets the element's display back to its original value. Our thinking
    was that this operation ought to approximate the cost of reflowing
    an element on a particular page since the browser would have to
    recalculate all the positions and sizes for every child within the
    element as well as make any changes to the overall document that
    this change would cause to parents and ancestors. This was
    originally the only test that the Reflow Timer performed, but as you
    can see from the results, we discovered that not all render engines
    work like Gecko's and so we began adding more tests.'''),
  ReflowTest('testVisibility', 'Visiblility None',
    '''Much like the display test above, this test sets an element's
    style.visibility="hidden" and then resets it back to its default,
    which is visible. Visually this has the same effect as setting
    display="none", however this change should be significantly less
    costly for the browser to perform as it should not need be purging
    the element from the render tree and recalculating
    geometries.'''),
  ReflowTest('testNonMatchingClass', 'Non Matching by Class',
    '''This test adds a class name to an element, and more specifically a
    class name which is not present in the document's CSS object
    model.'''),
  ReflowTest('testFourClassReflows', 'Four Reflows by Class',
    '''This test adds a class name to an element that will match a
    previously added CSS declaration added to the CSSOM. This
    declaration is set with four property value pairs which should in
    and of themselves be capable of causing a 1x reflow time. For
    instance, "font-size: 20px; line-height: 10px; padding-left: 10px;
    margin-top: 7px;". This test aims to test whether reflow
    operations occur in a single queue flush or if they are performed
    one at a time when these changes are made via a CSS
    classname. This test is a sort of opposite to the Four Reflows By
    Script.'''),
  ReflowTest('testFourScriptReflows', 'Four Reflows by Script',
    '''Like the Four Reflows By Class test, but instead this test has
    four lines of Javascript, each of which alters the style object
    with a property/value that by itself could cause a 1x reflow
    time.'''),
  ReflowTest('testTwoScriptReflows', 'Two Reflows by Script',
    '''Like the Four Reflows By Script test, except with only two lines
    of Javascript.'''),
  ReflowTest('testPaddingPx', 'Padding px',
    '''This test sets style.padding="FOOpx", aka padding on all sides of
    the box model.'''),
  ReflowTest('testPaddingLeftPx', 'Padding Left px',
    '''This test sets style.paddingLeft="FOOpx", aka padding on only the
    left side of the box.'''),
  ReflowTest('testFontSizeEm', 'Font Size em',
    '''This test changes an element's style.fontSize to an em-based
    value.'''),
  ReflowTest('testWidthPercent', 'Width %',
    '''This test sets an element's style.width="FOO%"'''),
  ReflowTest('testBackground', 'Background Color',
    '''This test sets an element's style.background="#FOO", aka a
    hexadecimal color.'''),
  ReflowTest('testOverflowHidden', 'Overflow Hidden',
    '''This test sets an element's style.overflow="hidden" and then back
    again, timing the cost of an element returning to the default
    overflow which is "visible"'''),
  ReflowTest('testSelectorMatchTime', 'Selector Match Time',
    '''In the world of reflow, one of the possible operations that can
    eat up time occurs when an element gets a new class name and the
    render engine has to look through the CSSOM to see if this element
    now matches some CSS declaration. The goal of this test is to try
    to cause this match operation without causing any change to the
    render tree. The test is much like the Non-Matching Class test,
    except that instead of flushing the render queue before this test,
    we try to simply flush any style computations. The way it works is
    to make a CSS rule that can get activated by virtue of an
    attribute selector but which will never cause reflow because it
    will never match any element in the render tree. For instance
    "body[bogusattr='bogusval'] html {color:pink}". We note that this
    test seems not to work in engines other than Gecko at this
    time.'''),
  ReflowTest('testGetOffsetHeight', 'Do Nothing / OffsetHeight',
    '''This test does nothing other than the request for offsetHeight
    after already having done so. Theoretically, this test is
    something like a control for our test and should have a 0 or very
    low time.'''),
)

TEST_SET = test_set_base.TestSet(
    category=_CATEGORY,
    category_name='Reflow',
    tests=_TESTS,
    subnav={
      'Test': '/%s/test' % _CATEGORY,
      'About': '/%s/about' % _CATEGORY
    },
    home_intro='''Reflow in a web browser refers to the process whereby the render engine calculates positions and geometries of elements in the document for purpose of drawing, or re-drawing, the visual presentation. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow times and also to understand the effects of various document properties (DOM nesting, CSS specificity, types of style changes) on reflow. Read more about reflow and these tests.''',
    default_params=[
    'nested_anchors', 'num_elements=400', 'num_nest=4',
    'css_selector=%23g-content%20*', 'num_css_rules=1000',
    'css_text=border%3A%201px%20solid%20%230C0%3B%20padding%3A%208px%3B'],
)
<|endoftext|>