egl-create-largest-pbuffer-surface: Don't call eglSwapBuffers
[piglit.git] / framework / backends / abstract.py
blob13a7b6707ea01788992bb9d7c92418172d4b41dc
1 # Copyright (c) 2014, 2016 Intel Corporation
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 # SOFTWARE.
22 """ Base classes for backends
24 This module provides mixins and base classes for backend modules.
26 """
28 from __future__ import (
29 absolute_import, division, print_function, unicode_literals
31 import abc
32 import contextlib
33 import itertools
34 import os
35 import shutil
37 import six
39 from framework import options
40 from . import compression
41 from framework.results import TestResult
42 from framework.status import INCOMPLETE
45 @contextlib.contextmanager
46 def write_compressed(filename):
47 """Write a the final result using desired compression.
49 This helper function reads the piglit.conf to decide whether to use
50 compression, and what type of compression to use.
52 Currently it implements no compression
54 """
55 mode = compression.get_mode()
56 if mode != 'none':
57 # if the suffix (final .xxx) is a knwon compression suffix
58 suffix = os.path.splitext(filename)[1]
59 if suffix in compression.COMPRESSION_SUFFIXES:
60 filename = '{}.{}'.format(os.path.splitext(filename)[0], mode)
61 else:
62 filename = '{}.{}'.format(filename, mode)
64 with compression.COMPRESSORS[mode](filename) as f:
65 yield f
68 @six.add_metaclass(abc.ABCMeta)
69 class Backend(object):
70 """ Abstract base class for summary backends
72 This class provides an abstract ancestor for classes implementing backends,
73 providing a light public API. The goal of this API is to be "just enough",
74 not a generic writing solution. To that end it provides two public methods,
75 'finalize', and 'write_test'. These two methods are designed to be just
76 enough to write a backend without needing format specific options.
78 Any locking that is necessary should be done in the child classes, as not
79 all potential backends need locking (for example, a SQL based backend might
80 be thread safe and not need to be locked during write)
82 """
83 @abc.abstractmethod
84 def __init__(self, dest, metadata, **kwargs):
85 """ Generic constructor
87 This method should setup the container and open any files or conections
88 as necissary. It should not however, write anything into the backend
89 store, that job is for the iniitalize method.
91 In addition it takes keyword arguments that define options for the
92 backends. Options should be prefixed to identify which backends they
93 apply to. For example, a json specific value should be passed as
94 json_*, while a file specific value should be passed as file_*)
96 Arguments:
97 dest -- the place to write the results to. This should be correctly
98 handled based on the backend, the example is calls open() on a
99 file, but other backends might want different options
103 @abc.abstractmethod
104 def initialize(self, metadata):
105 """ Write initial metadata and setup
107 This method is used to write metadata into the backend store and do any
108 other initial backend writing that is required. This method and the
109 finalize() method are bookends, one starts, the other finishes.
111 Arguments:
112 metadata -- a dict or dict-like object that contains metadata to be
113 written into the backend
117 @abc.abstractmethod
118 def finalize(self, metadata=None):
119 """ Write final metadata into to the store and close it
121 This method writes any final metadata into the store, what can be
122 written is implementation specific, backends are free to ignore any
123 data that is not applicable.
125 metadata is not required, and Backend derived classes need to handle
126 being passed None correctly.
128 Keyword Arguments:
129 metadata -- Any metadata to be written in after the tests, should be a
130 dict or dict-like object
135 @abc.abstractmethod
136 def write_test(self, name):
137 """ Write a test into the backend store
139 This method writes an actual test into the backend store.
141 Should be a context manager, used with the with statement. It should
142 first write an incomplete status value, then yield and object that will
143 overwrite that value with the final value. That object needs to take a
144 'data' parameter which is a result.TestResult object.
146 Arguments:
147 name -- the name of the test to be written
148 data -- a TestResult object representing the test data
153 class FileBackend(Backend):
154 """ A baseclass for file based backends
156 This class provides a few methods and setup required for a file based
157 backend.
159 Arguments:
160 dest -- a folder to store files in
162 Keyword Arguments:
163 file_start_count -- controls the counter for the test result files.
164 Whatever this is set to will be the first name of the
165 tests. It is important for resumes that this is not
166 overlapping as the Inheriting classes assume they are
167 not. Default: 0
170 def __init__(self, dest, file_start_count=0, **kwargs):
171 self._dest = dest
172 self._counter = itertools.count(file_start_count)
173 self._write_final = write_compressed
175 __INCOMPLETE = TestResult(result=INCOMPLETE)
177 def __fsync(self, file_):
178 """ Sync the file to disk
180 If options.OPTIONS.sync is truthy this will sync self._file to disk
183 file_.flush()
184 if options.OPTIONS.sync:
185 os.fsync(file_.fileno())
187 @abc.abstractmethod
188 def _write(self, f, name, data):
189 """Method that writes a TestResult into a result file."""
191 @abc.abstractproperty
192 def _file_extension(self):
193 """The file extension of the backend."""
195 @contextlib.contextmanager
196 def write_test(self, name):
197 """Write a test.
199 When this context manager is opened it will first write a placeholder
200 file with the status incomplete.
202 When it is called to write the finall result it will create a temporary
203 file, write to that file, then move that file over the original,
204 incomplete status file. This helps to make the operation atomic, as
205 long as the filesystem continues running and the result was valid in
206 the original file it will be valid at the end
209 def finish(val):
210 tfile = file_ + '.tmp'
211 with open(tfile, 'w') as f:
212 self._write(f, name, val)
213 self.__fsync(f)
214 shutil.move(tfile, file_)
216 file_ = os.path.join(self._dest, 'tests', '{}.{}'.format(
217 next(self._counter), self._file_extension))
219 with open(file_, 'w') as f:
220 self._write(f, name, self.__INCOMPLETE)
221 self.__fsync(f)
223 yield finish