# file test_fedora/base.py
# 
#   Copyright 2011 Emory University Libraries
#
#   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 os
import unittest
import logging
from eulxml import xmlmap
from eulfedora.api import ApiFacade
from eulfedora.server import Repository
from eulfedora.util import RequestFailed

from localsettings import FEDORA_ROOT, FEDORA_ROOT_NONSSL, \
     FEDORA_USER, FEDORA_PASSWORD, FEDORA_PIDSPACE

logger = logging.getLogger(__name__)

FIXTURE_ROOT = os.path.join(os.path.dirname(__file__), 'fixtures')
def fixture_path(fname):
    return os.path.join(FIXTURE_ROOT, fname)

def load_fixture_data(fname):
    with open(fixture_path(fname)) as f:
        return f.read()

class _MinimalFoxml(xmlmap.XmlObject):
    pid = xmlmap.StringField('@PID')

class FedoraTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        unittest.TestCase.__init__(self, *args, **kwargs)
        self.fedora_fixtures_ingested = []
        self.pidspace = FEDORA_PIDSPACE

    def setUp(self):
        self.repo = Repository(FEDORA_ROOT, FEDORA_USER, FEDORA_PASSWORD)
        # NOTE: queries require RI flush=True or test objects will not show up in RI
        self.repo.risearch.RISEARCH_FLUSH_ON_QUERY = True
        self.opener = self.repo.opener
        self.api = ApiFacade(self.opener)
        fixtures = getattr(self, 'fixtures', [])
        for fixture in fixtures:
            self.ingestFixture(fixture)

    def tearDown(self):
        for pid in self.fedora_fixtures_ingested:
            try: 
                self.repo.purge_object(pid)
            except RequestFailed as rf:
                logger.warn('Error purging test object %s in tear down: %s' % \
                            (pid, rf))

    def getNextPid(self):
        pidspace = getattr(self, 'pidspace', None)
        return self.repo.get_next_pid(namespace=pidspace)

    def loadFixtureData(self, fname):
        data = load_fixture_data(fname)
        # if pidspace is specified, get a new pid from fedora and set it as the pid in the xml 
        if hasattr(self, 'pidspace'):
            xml = xmlmap.load_xmlobject_from_string(data, _MinimalFoxml)            
            xml.pid = self.getNextPid()
            return xml.serialize()
        else:
            return data

    def ingestFixture(self, fname):
        object = self.loadFixtureData(fname)
        pid = self.repo.ingest(object)
        if pid:
            # we'd like this always to be true. if ingest fails we should
            # throw an exception. that probably hasn't been thoroughly
            # tested yet, though, so we'll check it until it has been.
            self.append_test_pid(pid)

    def append_test_pid(self, pid):
            self.fedora_fixtures_ingested.append(pid)

@@ -1509,29 +1509,8 @@
 CE%0A%0A
-    def setUp(self):%0A
     
@@ -1578,16 +1578,798 @@
 ASSWORD)
+%0A%0A        # fixture cleanup happens in tearDown, which doesn't always run%0A        # if a test fails - clean up stale test objects from a previous run here%0A        stale_objects = list(self.repo.find_objects(pid__contains='%25s:*' %25 self.pidspace))%0A        if stale_objects:%0A            print 'Removing %25d stale test object(s) in pidspase %25s' %25 (len(stale_objects),%0A                                                                       self.pidspace)%0A            for obj in stale_objects:%0A                try:%0A                    self.repo.purge_object(obj.pid)%0A                except RequestFailed as rf:%0A                    logger.warn('Error purging stale test object %25s (TestCase init): %25s' %25 %5C%0A                                (obj.pid, rf))%0A                %0A%0A    def setUp(self):
 %0A       

clean up stale test objects before running a new testcase