[pitivi] tests: Move all our integration testsuite to GstValidate
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] tests: Move all our integration testsuite to GstValidate
- Date: Thu, 11 Jun 2015 09:15:07 +0000 (UTC)
commit 1f101615de835781c3c382b753b3542dad1ed274
Author: Thibault Saunier <tsaunier gnome org>
Date: Fri Feb 13 16:47:58 2015 +0100
tests: Move all our integration testsuite to GstValidate
Summary: And start implementing a few simple tests for the timeline.
Reviewers: Mathieu_Du
Differential Revision: http://phabricator.freedesktop.org/D112
pitivi/application.py | 4 +-
pitivi/check.py | 2 +-
pitivi/project.py | 22 +-
pitivi/timeline/elements.py | 6 +-
pitivi/timeline/timeline.py | 49 ++-
pitivi/undo/timeline.py | 12 +-
pitivi/undo/undo.py | 3 +-
pitivi/utils/timeline.py | 6 +
pitivi/utils/validate.py | 433 +++++++++++++++--
pitivi/utils/widgets.py | 3 +
pitivi/viewer.py | 2 -
tests/dogtail_scripts/common.py | 485 --------------------
tests/dogtail_scripts/test_clipproperties.py | 114 -----
.../dogtail_scripts/test_dialogs_clipmediaprops.py | 76 ---
tests/dogtail_scripts/test_dialogs_prefs.py | 56 ---
.../dogtail_scripts/test_dialogs_startupwizard.py | 22 -
tests/dogtail_scripts/test_effects.py | 91 ----
tests/dogtail_scripts/test_medialibrary.py | 68 ---
tests/dogtail_scripts/test_project.py | 369 ---------------
tests/dogtail_scripts/test_timeline.py | 252 ----------
tests/samples/30fps_numeroted_frames_blue.webm | Bin 0 -> 82107 bytes
tests/samples/one_fps_numeroted_blue.mkv | Bin 0 -> 405852 bytes
tests/test_utils.py | 2 +-
tests/validate-tests/pitivi.py | 35 ++
tests/validate-tests/pitivivalidate.py | 125 +++++
tests/validate-tests/runtests | 17 +
tests/validate-tests/select_clip.scenario | 18 +
tests/validate-tests/simple_play.scenario | 13 +
tests/validate-tests/simple_ripple.scenario | 126 +++++
tests/validate-tests/simple_split_clips.scenario | 17 +
30 files changed, 805 insertions(+), 1623 deletions(-)
---
diff --git a/pitivi/application.py b/pitivi/application.py
index c2eb101..01a819e 100644
--- a/pitivi/application.py
+++ b/pitivi/application.py
@@ -42,6 +42,7 @@ from pitivi.dialogs.startupwizard import StartUpWizard
from pitivi.utils.misc import quote_uri, path_from_uri
from pitivi.utils.system import getSystem
from pitivi.utils.loggable import Loggable
+from pitivi.utils.timeline import Zoomable
import pitivi.utils.loggable as log
@@ -88,6 +89,7 @@ class Pitivi(Gtk.Application, Loggable):
self._scenario_file = None
self._first_action = True
+ Zoomable.app = self
self.connect("startup", self._startupCb)
self.connect("activate", self._activateCb)
self.connect("open", self.openCb)
@@ -224,8 +226,6 @@ class Pitivi(Gtk.Application, Loggable):
self.quit()
return True
- self._first_action = True
-
def _setScenarioFile(self, uri):
if 'PITIVI_SCENARIO_FILE' in os.environ:
uri = quote_uri(os.environ['PITIVI_SCENARIO_FILE'])
diff --git a/pitivi/check.py b/pitivi/check.py
index a3d3627..c427427 100644
--- a/pitivi/check.py
+++ b/pitivi/check.py
@@ -263,7 +263,7 @@ def initialize_modules():
from gi.repository import Gst
Gst.init(None)
from gi.repository import GES
- GES.init()
+ res, sys.argv = GES.init_check(sys.argv)
from pitivi.utils import validate
validate.init()
diff --git a/pitivi/project.py b/pitivi/project.py
index 7ef75ad..9da0cbd 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -40,7 +40,7 @@ from pwd import getpwuid
from pitivi.undo.undo import UndoableAction
from pitivi.configure import get_ui_dir
-from pitivi.utils.validate import has_validate
+from pitivi.utils.validate import has_validate, create_monitor
from pitivi.utils.misc import quote_uri, path_from_uri, isWritable, unicode_error_dialog
from pitivi.utils.pipeline import PipelineError, Seeker
from pitivi.utils.loggable import Loggable
@@ -189,6 +189,7 @@ class ProjectManager(GObject.Object, Loggable):
self.current_project = None
self.disable_save = False
self._backup_lock = 0
+ self.exitcode = 0
def _tryUsingBackupFile(self, uri):
backup_path = self._makeBackupURI(path_from_uri(uri))
@@ -317,7 +318,7 @@ class ProjectManager(GObject.Object, Loggable):
# Load the project:
self.current_project = Project(self.app, uri=uri, scenario=scenario)
- self.current_project.connect("missing-uri", self._missingURICb)
+ self.current_project.connect_after("missing-uri", self._missingURICb)
self.current_project.connect("loaded", self._projectLoadedCb)
if self.current_project.createTimeline():
@@ -564,7 +565,7 @@ class ProjectManager(GObject.Object, Loggable):
self.debug(
"Tried disconnecting signals, but they were not connected")
self._cleanBackup(self.current_project.uri)
- self.current_project.release()
+ self.exitcode = self.current_project.release()
self.current_project = None
return True
@@ -800,12 +801,14 @@ class Project(Loggable, GES.Project):
self.info("Setting up validate scenario")
self.runner = GstValidate.Runner.new()
+ create_monitor(self.runner, self.app.gui)
self.monitor = GstValidate.Monitor.factory_create(
self.pipeline, self.runner, None)
self._scenario = GstValidate.Scenario.factory_create(
self.runner, self.pipeline, self.scenario)
self.pipeline.setForcePositionListener(True)
self._scenario.connect("done", self._scenarioDoneCb)
+ self._scenario.props.execute_on_idle = True
# --------------- #
# Our properties #
@@ -1028,8 +1031,9 @@ class Project(Loggable, GES.Project):
return
self.nb_imported_files += 1
assets = self.get_loading_assets()
- self.nb_remaining_file_to_import = len([asset for asset in assets if
- GObject.type_is_a(asset.get_extractable_type(),
GES.UriClip)])
+ self.nb_remaining_file_to_import = len([tmpasset for tmpasset in assets if
+ GObject.type_is_a(tmpasset.get_extractable_type(),
+ GES.UriClip)])
if self.nb_remaining_file_to_import == 0:
self.nb_imported_files = 0
# We do not take into account asset comming from project
@@ -1155,19 +1159,23 @@ class Project(Loggable, GES.Project):
return self.list_assets(GES.UriClip)
def release(self):
- if self.runner:
- self.runner.printf()
+ res = 0
if self.pipeline:
self.pipeline.release()
if self.runner:
+ res = self.runner.printf()
+
+ if self.runner:
self.runner = None
self.monitor = None
self.pipeline = None
self.timeline = None
+ return res
+
def setModificationState(self, state):
self._dirty = state
if state:
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index a91db08..9836b9d 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -566,10 +566,12 @@ class Clip(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
pass
def sendFakeEvent(self, event, event_widget):
+ follow_up = True
if event.type == Gdk.EventType.BUTTON_RELEASE:
- self._clickedCb(event_widget, event)
+ follow_up = self._clickedCb(event_widget, event)
- self.timeline.sendFakeEvent(event, event_widget)
+ if follow_up:
+ self.timeline.sendFakeEvent(event, event_widget)
def do_draw(self, cr):
self.updatePosition()
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 508eac0..5cdd616 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -170,7 +170,7 @@ class Marquee(Gtk.Box, Loggable):
else:
res.append(clip)
- self.debug("Selected clips: %s" % res)
+ self.debug("Result is %s" % res)
return tuple(set(res))
@@ -279,7 +279,7 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
self.get_accessible().set_name("timeline canvas")
self.__fake_event_widget = None
- def sendFakeEvent(self, event, event_widget):
+ def sendFakeEvent(self, event, event_widget=None):
# Member usefull for testsing
self.__fake_event_widget = event_widget
@@ -290,6 +290,8 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
self.__buttonReleaseEventCb(self, event)
elif event.type == Gdk.EventType.MOTION_NOTIFY:
self.__motionNotifyEventCb(self, event)
+ else:
+ self.parent.sendFakeEvent(event)
self.__fake_event_widget = None
@@ -387,10 +389,8 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
self.layout.move(self.__snap_bar, self.nsToPixel(position), 0)
self.__snap_bar.show()
self.__snap_position = position
- self.debug("-> Snap START!")
def hideSnapBar(self):
- self.debug("-> Force hiding snap bar")
self.__snap_position = 0
self.__snap_bar.hide()
@@ -784,13 +784,14 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
# Edition handling
def __setupTimelineEdition(self):
self.draggingElement = None
- self.__editing_context = None
+ self.editing_context = None
self.__got_dragged = False
self.__drag_start_x = 0
self.__on_separators = []
self._on_layer = None
def __getLayerAt(self, y, bLayer=None):
+ self.error("Y is %s" % y)
if y < 20 or not self.bTimeline.get_layers():
try:
bLayer = self.bTimeline.get_layers()[0]
@@ -844,22 +845,22 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
if self.__got_dragged is False:
self.__got_dragged = True
self.allowSeek = False
- self.__editing_context = timelineUtils.EditingContext(self.draggingElement.bClip,
- self.bTimeline,
- self.draggingElement.edit_mode,
- self.draggingElement.dragging_edge,
- None,
- self.app.action_log)
+ self.editing_context = timelineUtils.EditingContext(self.draggingElement.bClip,
+ self.bTimeline,
+ self.draggingElement.edit_mode,
+ self.draggingElement.dragging_edge,
+ None,
+ self.app.action_log)
x, y = event_widget.translate_coordinates(self, x, y)
x -= ui.CONTROL_WIDTH
x += self.hadj.get_value()
y += self.vadj.get_value()
- mode = self.get_parent().getEditionMode(isAHandle=self.__editing_context.edge != GES.Edge.EDGE_NONE)
- self.__editing_context.setMode(mode)
+ mode = self.get_parent().getEditionMode(isAHandle=self.editing_context.edge != GES.Edge.EDGE_NONE)
+ self.editing_context.setMode(mode)
- if self.__editing_context.edge is GES.Edge.EDGE_END:
+ if self.editing_context.edge is GES.Edge.EDGE_END:
position = self.pixelToNs(x)
else:
position = self.pixelToNs(x - self.__drag_start_x)
@@ -873,10 +874,10 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
if self.__on_separators:
self.__setHoverSeparators()
- self.__editing_context.editTo(position, priority)
+ self.editing_context.editTo(position, priority)
def createLayer(self, priority):
- self.info("Creating layer %s" % priority)
+ self.error("Creating layer %s" % priority)
new_bLayer = GES.Layer.new()
new_bLayer.props.priority = priority
self.bTimeline.add_layer(new_bLayer)
@@ -917,16 +918,17 @@ class Timeline(Gtk.EventBox, timelineUtils.Zoomable, Loggable):
if self.__on_separators[0] == self._on_layer.ui.after_sep:
priority = self._on_layer.props.priority + 1
+ self.error("On separator --> %s" % priority)
self.createLayer(max(0, priority))
self._onSeparatorStartTime = None
- self.__editing_context.editTo(self.__editing_context.new_position, priority)
+ self.editing_context.editTo(self.editing_context.new_position, priority)
self.layout.props.width = self._computeTheoricalWidth()
- self.__editing_context.finish()
+ self.editing_context.finish()
self.draggingElement = None
self.__got_dragged = False
- self.__editing_context = None
+ self.editing_context = None
self.hideSnapBar()
self.__unsetHoverSeparators()
@@ -1034,7 +1036,7 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
def zoomFit(self):
# self._hscrollbar.set_value(0)
- self.app.write_action("set-zoom-fit", {"not-mandatory-action-type": True})
+ self.app.write_action("zoom-fit", {"not-mandatory-action-type": True})
self._setBestZoomRatio(allow_zoom_in=True)
@@ -1532,6 +1534,13 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
# Gtk widget virtual methods
+ def sendFakeEvent(self, event):
+ self.info("Faking %s" % event)
+ if event.type == Gdk.EventType.KEY_PRESS:
+ self.do_key_press_event(event)
+ elif event.type == Gdk.EventType.KEY_RELEASE:
+ self.do_key_release_event(event)
+
def do_key_press_event(self, event):
# This is used both for changing the selection modes and for affecting
# the seek keyboard shortcuts further below
diff --git a/pitivi/undo/timeline.py b/pitivi/undo/timeline.py
index 465d06f..62b2ea9 100644
--- a/pitivi/undo/timeline.py
+++ b/pitivi/undo/timeline.py
@@ -323,6 +323,8 @@ class ClipAdded(UndoableAction):
if hasattr(self.layer, "splitting_object") and \
self.layer.splitting_object is True:
return None
+ elif self.layer.get_timeline().ui.editing_context is not None:
+ return None
st = Gst.Structure.new_empty("add-clip")
st.set_value("name", self.clip.get_name())
@@ -354,6 +356,9 @@ class ClipRemoved(UndoableAction):
self._undone()
def asScenarioAction(self):
+ if self.layer.get_timeline().ui.editing_context is not None:
+ return None
+
st = Gst.Structure.new_empty("remove-clip")
st.set_value("name", self.clip.get_name())
return st
@@ -580,7 +585,8 @@ class TimelineLogObserver(Loggable):
def _connectToTrackElement(self, track_element):
for prop, binding in track_element.get_all_control_bindings().items():
- self._connectToControlSource(track_element, binding)
+ self._connectToControlSource(track_element, binding,
+ existed=True)
track_element.connect("control-binding-added",
self._controlBindingAddedCb)
if isinstance(track_element, GES.BaseEffect):
@@ -592,7 +598,7 @@ class TimelineLogObserver(Loggable):
for prop, binding in track_element.get_all_control_bindings().items():
self._disconnectFromControlSource(binding)
- def _connectToControlSource(self, track_element, binding):
+ def _connectToControlSource(self, track_element, binding, existed=False):
control_source = binding.props.control_source
control_source.connect("value-added",
@@ -610,7 +616,7 @@ class TimelineLogObserver(Loggable):
tracker.connect("keyframe-moved", self._controlSourceKeyFrameMovedCb)
self.control_source_keyframe_trackers[control_source] = tracker
- if self.log.app:
+ if self.log.app and not existed:
self.log.app.write_action("set-control-source",
{"element-name": track_element.get_name(),
"property-name": binding.props.name,
diff --git a/pitivi/undo/undo.py b/pitivi/undo/undo.py
index 4eb1590..c6f803f 100644
--- a/pitivi/undo/undo.py
+++ b/pitivi/undo/undo.py
@@ -42,7 +42,7 @@ class UndoWrongStateError(UndoError):
pass
-class UndoableAction(GObject.Object):
+class UndoableAction(GObject.Object, Loggable):
"""
An action that can be undone.
@@ -57,6 +57,7 @@ class UndoableAction(GObject.Object):
def __init__(self):
GObject.Object.__init__(self)
+ Loggable.__init__(self)
def do(self):
raise NotImplementedError()
diff --git a/pitivi/utils/timeline.py b/pitivi/utils/timeline.py
index 854396c..dcff246 100644
--- a/pitivi/utils/timeline.py
+++ b/pitivi/utils/timeline.py
@@ -328,6 +328,8 @@ class Zoomable(object):
_cur_zoom = 20
zoomratio = None
+ app = None
+
def __init__(self):
# FIXME: ideally we should deprecate this
Zoomable.addInstance(self)
@@ -367,10 +369,14 @@ class Zoomable(object):
@classmethod
def zoomIn(cls):
cls.setZoomLevel(cls._cur_zoom + 1)
+ cls.app.write_action("zoom-in",
+ {"not-mandatory-action-type": True})
@classmethod
def zoomOut(cls):
cls.setZoomLevel(cls._cur_zoom - 1)
+ cls.app.write_action("zoom-out",
+ {"not-mandatory-action-type": True})
@classmethod
def computeZoomRatio(cls, x):
diff --git a/pitivi/utils/validate.py b/pitivi/utils/validate.py
index 3583082..b69657a 100644
--- a/pitivi/utils/validate.py
+++ b/pitivi/utils/validate.py
@@ -18,8 +18,10 @@
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
-import sys
+import locale
+import subprocess
+from gi.repository import Gtk
from gi.repository import Gst
from gi.repository import GES
from gi.repository import Gdk
@@ -28,62 +30,256 @@ from gi.repository import GLib
from pitivi.utils import ui
from pitivi.utils import timeline as timelineUtils
+
+CAT = "validate"
+
+
try:
from gi.repository import GstValidate
except ImportError:
GstValidate = None
+monitor = None
has_validate = False
+def Event(event_type, **kwargs):
+ event_types_constructors = {
+ Gdk.EventType.BUTTON_PRESS: Gdk.EventButton.new,
+ Gdk.EventType.BUTTON_RELEASE: Gdk.EventButton.new,
+ Gdk.EventType.MOTION_NOTIFY: Gdk.EventMotion.new
+ }
+
+ try:
+ event = event_types_constructors[event_type](event_type)
+ except KeyError:
+ event = Gdk.Event.new(event_type)
+
+ for arg, value in kwargs.items():
+ setattr(event, arg, value)
+
+ return event
+
+if GstValidate:
+ class PitiviMonitor(GstValidate.Monitor):
+ def __init__(self, runner, object):
+ GstValidate.Monitor.__init__(self, object=object, validate_runner=runner)
+
+ if GstValidate:
+ try:
+ from gi.repository import Wnck
+ Wnck.Screen.get_default().connect("window-opened", self._windowOpenedCb)
+ except ImportError:
+ print("Wnck not present on the system,"
+ " not checking the sink does not open a new window")
+ pass
+
+ def _windowOpenedCb(self, screen, window):
+ global monitor
+
+ if window.get_name() == 'OpenGL renderer' and monitor:
+ monitor.report_simple(GLib.quark_from_string("pitivi::wrong-window-creation"),
+ "New window created by the sink,"
+ " that should not happen")
+
+ def checkWrongWindow(self):
+ try:
+ windows = subprocess.check_output(["xwininfo", "-tree",
"-root"]).decode(locale.getdefaultlocale()[1])
+ for w in windows.split('\n'):
+ if "OpenGL renderer" in w and w.startswith(" 0x"):
+ monitor.report_simple(GLib.quark_from_string("pitivi::wrong-window-creation"),
+ "New window created by the sink,"
+ " that should not happen, (current windows: %s)"
+ % windows)
+ break
+ except subprocess.CalledProcessError:
+ pass
+
+
+def create_monitor(runner, app):
+ global monitor
+ global has_validate
+
+ if not monitor and has_validate:
+ monitor = PitiviMonitor(runner, app)
+ GstValidate.Reporter.set_name(monitor, "Pitivi")
+
+
def stop(scenario, action):
+ global monitor
+
+ if monitor:
+ monitor.checkWrongWindow()
+
if action.structure.get_boolean("force")[0]:
+ GstValidate.execute_action(GstValidate.get_action_type(action.type).overriden_type,
+ action)
+
timeline = scenario.pipeline.props.timeline
project = timeline.get_asset()
if project:
project.setModificationState(False)
GstValidate.print_action(action, "Force quiting, ignoring any"
- " changes in the project")
+
+ " changes in the project\n")
timeline.ui.app.shutdown()
return 1
- GstValidate.print_action(action, "not doing anything in pitivi")
+ GstValidate.print_action(action, "STOP: not doing anything in pitivi\n")
return 1
-def editContainer(scenario, action):
- # edit-container, edge=(string)edge_end, position=(double)2.2340325289999998,
edit-mode=(string)edit_trim, container-name=(string)uriclip0, new-layer-priority=(int)-1;
- timeline = scenario.pipeline.props.timeline
- container = timeline.get_element(action.structure["container-name"])
+def set_state(scenario, action):
+ wanted_state = action.structure["state"]
+ if wanted_state is None:
+ wanted_state = action.structure.get_name()
+ if wanted_state == "play":
+ wanted_state = "playing"
+ elif wanted_state == "pause":
+ wanted_state = "paused"
+
+ if wanted_state == "paused":
+ if scenario.__dict__.get("started", None) is None:
+
+ return 1
+ return GstValidate.execute_action(GstValidate.get_action_type(action.type).overriden_type,
+ action)
+
+
+def get_edge(structure):
try:
- res, edge = GstValidate.utils_enum_from_str(GES.Edge, action.structure["edge"])
+ res, edge = GstValidate.utils_enum_from_str(GES.Edge, structure["edge"])
if not res:
edge = GES.Edge.EDGE_NONE
else:
edge = GES.Edge(edge)
+
except KeyError:
edge = GES.Edge.EDGE_NONE
+ return edge
+
+
+def _releaseButtonIfNeeded(scenario, action, timeline, container, edge, layer_prio,
+ position, y):
+ try:
+ next_actions = scenario.get_actions()
+ for next_action in next_actions[1:]:
+ if next_action.type not in ["wait", "add-layer"]:
+ break
+ except KeyError:
+ return
+
+ if next_action is None or next_action.type != "edit-container":
+ scenario.dragging = False
+ event = Gdk.EventButton.new(Gdk.EventType.BUTTON_RELEASE)
+ event.button = 1
+ event.x = timelineUtils.Zoomable.nsToPixelAccurate(position)
+ event.y = y
+ container.ui.sendFakeEvent(event, container.ui)
+
+ if isinstance(container, GES.SourceClip):
+ if edge == GES.Edge.EDGE_START:
+ container.ui.leftHandle._eventCb(container.ui.leftHandle,
Gdk.Event.new(Gdk.EventType.LEAVE_NOTIFY))
+ if edge == GES.Edge.EDGE_END:
+ container.ui.rightHandle._eventCb(container.ui.rightHandle,
Gdk.Event.new(Gdk.EventType.LEAVE_NOTIFY))
+
+ if layer_prio > 0 and container.get_layer().get_priority() != layer_prio:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Resulting clip priority: %s"
+ " is not the same as the wanted one: %s"
+ % (container.get_layer().get_priority(),
+ layer_prio))
+
+ cleanEditModes(timeline, scenario)
+
+
+def cleanEditModes(timeline, scenario):
+ if scenario.last_mode == GES.EditMode.EDIT_RIPPLE:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_RELEASE, keyval=Gdk.KEY_Shift_L))
+ elif scenario.last_mode == GES.EditMode.EDIT_ROLL:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_RELEASE, keyval=Gdk.KEY_Control_L))
+
+ scenario.last_mode = None
+
+
+def setEditingMode(timeline, scenario, action):
+ try:
+ mode = scenario.last_mode
+ mode
+ except AttributeError:
+ scenario.last_mode = None
+
+ try:
+ res, mode = GstValidate.utils_enum_from_str(GES.EditMode, action.structure["edit-mode"])
+ if not res:
+ mode = GES.EditMode.EDIT_NORMAL
+ else:
+ mode = GES.EditMode(mode)
+ except KeyError:
+ mode = GES.EditMode.EDIT_NORMAL
+
+ if mode == GES.EditMode.EDIT_RIPPLE:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_PRESS, keyval=Gdk.KEY_Shift_L))
+
+ if scenario.last_mode == GES.EditMode.EDIT_ROLL:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_RELEASE, keyval=Gdk.KEY_Control_L))
+
+ elif mode == GES.EditMode.EDIT_ROLL:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_PRESS, keyval=Gdk.KEY_Control_L))
+
+ if scenario.last_mode == GES.EditMode.EDIT_RIPPLE:
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_RELEASE, keyval=Gdk.KEY_Shift_L))
+ else:
+ cleanEditModes(timeline, scenario)
+
+ scenario.last_mode = mode
+
+
+def editContainer(scenario, action):
+ timeline = scenario.pipeline.props.timeline
+ container = timeline.get_element(action.structure["container-name"])
+
+ if container is None:
+ for layer in timeline.get_layers():
+ for clip in layer.get_clips():
+ Gst.info("Exisiting clip: %s" % clip.get_name())
+
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Could not find container: %s"
+ % action.structure["container-name"])
+
+ return 1
+
res, position = GstValidate.action_get_clocktime(scenario, action, "position")
layer_prio = action.structure["new-layer-priority"]
if res is False:
return 0
+ edge = get_edge(action.structure)
container_ui = container.ui
- y = 21
- if container.get_layer().get_priority() != layer_prio:
+ setEditingMode(timeline, scenario, action)
+
+ y = 21 - container_ui.translate_coordinates(timeline.ui, 0, 0)[1]
+
+ if container.get_layer().get_priority() != layer_prio and layer_prio != -1:
try:
layer = timeline.get_layers()[layer_prio]
+ Gst.info("Y is: %s Realized?? %s Priori: %s layer prio: %s"
+ % (layer.ui.get_allocation().y,
+ container_ui.get_realized(),
+ container.get_layer().get_priority(),
+ layer_prio))
y = layer.ui.get_allocation().y - container_ui.translate_coordinates(timeline.ui, 0, 0)[1]
if y < 0:
y += 21
- else:
+ elif y > 0:
y -= 21
except IndexError:
if layer_prio == -1:
@@ -96,53 +292,36 @@ def editContainer(scenario, action):
if not hasattr(scenario, "dragging") or scenario.dragging is False:
if isinstance(container, GES.SourceClip):
if edge == GES.Edge.EDGE_START:
- container.ui.leftHandle._eventCb(Gdk.Event.new(Gdk.Event.ENTER_NOTIFY))
+ container.ui.leftHandle._eventCb(container.ui.leftHandle,
Gdk.Event.new(Gdk.EventType.ENTER_NOTIFY))
elif edge == GES.Edge.EDGE_END:
- container.ui.leftHandle._eventCb(Gdk.Event.new(Gdk.Event.ENTER_NOTIFY))
+ container.ui.rightHandle._eventCb(container.ui.rightHandle,
Gdk.Event.new(Gdk.EventType.ENTER_NOTIFY))
scenario.dragging = True
- event = Gdk.EventButton.new(Gdk.EventType.BUTTON_PRESS)
- event.button = 1
- event.y = y
+ event = Event(Gdk.EventType.BUTTON_PRESS, button=1, y=y)
container.ui.sendFakeEvent(event, container.ui)
- event = Gdk.EventMotion.new(Gdk.EventType.MOTION_NOTIFY)
- event.button = 1
- event.x = timelineUtils.Zoomable.nsToPixelAccurate(position) -
container_ui.translate_coordinates(timeline.ui, 0, 0)[0] + ui.CONTROL_WIDTH
- event.y = y
- event.state = Gdk.ModifierType.BUTTON1_MASK
+ event = Event(Gdk.EventType.MOTION_NOTIFY, button=1,
+ x=timelineUtils.Zoomable.nsToPixelAccurate(position) -
+ container_ui.translate_coordinates(timeline.ui, 0, 0)[0] + ui.CONTROL_WIDTH,
+ y=y, state=Gdk.ModifierType.BUTTON1_MASK)
container.ui.sendFakeEvent(event, container.ui)
GstValidate.print_action(action, "Editing %s to %s in %s mode, edge: %s "
- "with new layer prio: %d\n" % (action.structure["container-name"],
- Gst.TIME_ARGS(position),
- timeline.ui.draggingElement.edit_mode,
- timeline.ui.draggingElement.dragging_edge,
- layer_prio))
+ "with new layer prio: %d\n" % (action.structure["container-name"],
+ Gst.TIME_ARGS(position),
+ timeline.ui.draggingElement.edit_mode,
+ timeline.ui.draggingElement.dragging_edge,
+ layer_prio))
- next_action = scenario.get_next_action()
- if next_action is None or next_action.type != "edit-container":
- scenario.dragging = False
- event = Gdk.EventButton.new(Gdk.EventType.BUTTON_RELEASE)
- event.button = 1
- event.x = timelineUtils.Zoomable.nsToPixelAccurate(position)
- event.y = y
- container.ui.sendFakeEvent(event, container.ui)
+ _releaseButtonIfNeeded(scenario, action, timeline, container, edge, layer_prio,
+ position, y)
- if isinstance(container, GES.SourceClip):
- if edge == GES.Edge.EDGE_START:
- container.ui.leftHandle._eventCb(Gdk.Event.new(Gdk.Event.LEAVE_NOTIFY))
- if edge == GES.Edge.EDGE_END:
- container.ui.leftHandle._eventCb(Gdk.Event.new(Gdk.Event.LEAVE_NOTIFY))
+ return 1
- if container.get_layer().get_priority() != layer_prio:
- scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
- "Resulting clip priority: %s"
- " is not the same as the wanted one: %s"
- % (container.get_layer().get_priority(),
- layer_prio))
- return 1
+# def commit(scenario, action):
+
+# return True
def splitClip(scenario, action):
@@ -152,13 +331,122 @@ def splitClip(scenario, action):
return True
-def setZoomFit(scenario, action):
+def zoom(scenario, action):
timeline = scenario.pipeline.props.timeline.ui
- timeline.parent.zoomFit()
+
+ GstValidate.print_action(action, action.type.replace('-', ' ') + "\n")
+
+ {"zoom-fit": timeline.parent.zoomFit,
+ "zoom-out": timelineUtils.Zoomable.zoomOut,
+ "zoom-in": timelineUtils.Zoomable.zoomIn}[action.type]()
+
+ return True
+
+
+def setZoomLevel(scenario, action):
+ timelineUtils.Zoomable.setZoomLevel(action.structure["level"])
return True
+def add_layer(scenario, action):
+ timeline = scenario.pipeline.props.timeline
+ if len(timeline.get_layers()) == 0:
+ GstValidate.print_action(action, "Adding first layer\n")
+ timeline.append_layer()
+ else:
+ GstValidate.print_action(action, "Not adding layer, should be done by pitivi itself\n")
+
+ return True
+
+
+def remove_clip(scenario, action):
+ try:
+ next_action = scenario.get_actions()[1]
+ except KeyError:
+ next_action = None
+
+ if next_action and next_action.type == "add-clip":
+ if next_action.structure["element-name"] == action.structure["element-name"]:
+ scenario.no_next_add_element = True
+ GstValidate.print_action(action,
+ "Just moving %s between layers, not removing it\n"
+ % action.structure["element-name"])
+ return True
+
+ action_type = GstValidate.get_action_type(action.type)
+
+ return GstValidate.execute_action(action_type.overriden_type, action)
+
+
+def select_clips(scenario, action):
+ should_select = True
+ timeline = scenario.pipeline.props.timeline
+ clip = timeline.get_element(action.structure["clip-name"])
+
+ if clip is None:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Could not find container: %s"
+ % action.structure["container-name"])
+
+ return 1
+
+ mode = action.structure["mode"]
+ if mode:
+ mode = mode.lower()
+
+ if mode == "ctrl":
+ if clip.ui.get_state_flags() & Gtk.StateFlags.SELECTED:
+ should_select = False
+
+ timeline.ui.sendFakeEvent(Event(event_type=Gdk.EventType.KEY_PRESS,
+ keyval=Gdk.KEY_Control_L))
+
+ event = Gdk.EventButton.new(Gdk.EventType.BUTTON_RELEASE)
+ clip.ui.sendFakeEvent(event, clip.ui)
+
+ selection = action.structure["selection"]
+ if not selection:
+ if should_select:
+ if not clip.ui.get_state_flags() & Gtk.StateFlags.SELECTED:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Clip %s should be selected but is not"
+ % clip.get_name())
+ elif clip.ui.get_state_flags() & Gtk.StateFlags.SELECTED:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Clip %s should be UNselected but is not"
+ % clip.get_name())
+ else:
+ for l in timeline.get_layers():
+ for c in l.get_clips():
+ if c.get_name() in selection:
+ if not c.ui.get_state_flags() & Gtk.StateFlags.SELECTED:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Clip %s should be selected (as defined in selection %s)"
+ " but is not" % (selection, clip.get_name()))
+ else:
+ if c.ui.get_state_flags() & Gtk.StateFlags.SELECTED:
+ scenario.report_simple(GLib.quark_from_string("scenario::execution-error"),
+ "Clip %s should NOT be selected (as defined in selection %s)"
+ " but it is" % (selection, clip.get_name()))
+
+ if mode == "ctrl":
+ timeline.ui.sendFakeEvent(Event(Gdk.EventType.KEY_RELEASE, keyval=Gdk.KEY_Control_L))
+
+ return 1
+
+
+def Parametter(name, desc, mandatory=False, possible_variables=None, types=None):
+ p = GstValidate.ActionParameter()
+ p.description = desc
+ p.mandatory = mandatory
+ p.name = name
+ p.possible_variables = possible_variables
+ p.types = types
+
+ return p
+
+
def init():
global has_validate
try:
@@ -170,6 +458,21 @@ def init():
"Pitivi override for the stop action",
GstValidate.ActionTypeFlags.NONE)
+ GstValidate.register_action_type("pause", "pitivi",
+ set_state, None,
+ "Pitivi override for the pause action",
+ GstValidate.ActionTypeFlags.NONE)
+
+ GstValidate.register_action_type("play", "pitivi",
+ set_state, None,
+ "Pitivi override for the pause action",
+ GstValidate.ActionTypeFlags.NONE)
+
+ GstValidate.register_action_type("set-state", "pitivi",
+ set_state, None,
+ "Pitivi override for the pause action",
+ GstValidate.ActionTypeFlags.NONE)
+
GstValidate.register_action_type("edit-container", "pitivi",
editContainer, None,
"Start dragging a clip in the timeline",
@@ -179,9 +482,37 @@ def init():
splitClip, None,
"Split a clip",
GstValidate.ActionTypeFlags.NONE)
- GstValidate.register_action_type("set-zoom-fit", "pitivi",
- setZoomFit, None,
- "Split a clip",
+
+ GstValidate.register_action_type("add-layer", "pitivi",
+ add_layer, None,
+ "Add layer",
+ GstValidate.ActionTypeFlags.NONE)
+
+ GstValidate.register_action_type("remove-clip", "pitivi",
+ remove_clip, None,
+ "Remove clip",
+ GstValidate.ActionTypeFlags.NONE)
+
+ GstValidate.register_action_type("select-clips", "pitivi",
+ select_clips, [Parametter("clip-name",
+ "The name of the clip to select",
+ True, None, "str")],
+ "Select clips",
+ GstValidate.ActionTypeFlags.NONE)
+
+ for z in ["zoom-fit", "zoom-out", "zoom-in"]:
+ GstValidate.register_action_type(z, "pitivi", zoom, None, z,
+ GstValidate.ActionTypeFlags.NO_EXECUTION_NOT_FATAL)
+ GstValidate.register_action_type('set-zoom-level', "pitivi", setZoomLevel, None, z,
GstValidate.ActionTypeFlags.NO_EXECUTION_NOT_FATAL)
+
+ Gst.info("Adding pitivi::wrong-window-creation")
+ GstValidate.Issue.register(GstValidate.Issue.new(
+ GLib.quark_from_string("pitivi::wrong-window-creation"),
+ "A new window for the sink has wrongly been created",
+ "All sink should display their images in an embedded "
+ "widget and thus not create a new window",
+ GstValidate.ReportLevel.CRITICAL))
+
except ImportError:
has_validate = False
diff --git a/pitivi/utils/widgets.py b/pitivi/utils/widgets.py
index 645ebf8..9c96dac 100644
--- a/pitivi/utils/widgets.py
+++ b/pitivi/utils/widgets.py
@@ -1073,6 +1073,9 @@ class ZoomBox(Gtk.Grid, Zoomable):
def _zoomAdjustmentChangedCb(self, adjustment):
Zoomable.setZoomLevel(adjustment.get_value())
+ self.timeline.app.write_action("set-zoom-level",
+ {"level": adjustment.get_value(),
+ "not-mandatory-action-type": True})
if self._manual_set is False:
self.timeline.scrollToPlayhead()
diff --git a/pitivi/viewer.py b/pitivi/viewer.py
index 5fa035d..c681fe2 100644
--- a/pitivi/viewer.py
+++ b/pitivi/viewer.py
@@ -38,8 +38,6 @@ from pitivi.utils.pipeline import AssetPipeline, Seeker
from pitivi.utils.ui import SPACING, hex_to_rgb
from pitivi.utils.widgets import TimeWidget
-import platform
-
GlobalSettings.addConfigSection("viewer")
GlobalSettings.addConfigOption("viewerDocked", section="viewer",
key="docked",
diff --git a/tests/samples/30fps_numeroted_frames_blue.webm b/tests/samples/30fps_numeroted_frames_blue.webm
new file mode 100644
index 0000000..c7aa550
Binary files /dev/null and b/tests/samples/30fps_numeroted_frames_blue.webm differ
diff --git a/tests/samples/one_fps_numeroted_blue.mkv b/tests/samples/one_fps_numeroted_blue.mkv
new file mode 100644
index 0000000..4e866b7
Binary files /dev/null and b/tests/samples/one_fps_numeroted_blue.mkv differ
diff --git a/tests/test_utils.py b/tests/test_utils.py
index b2b2029..618ec19 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -25,7 +25,7 @@ from unittest import TestCase
from gi.repository import Gst
from pitivi.utils.ui import beautify_length
-from pitivi.check import *
+from pitivi.check import * # noqa
second = Gst.SECOND
minute = second * 60
diff --git a/tests/validate-tests/pitivi.py b/tests/validate-tests/pitivi.py
new file mode 100644
index 0000000..d36d23d
--- /dev/null
+++ b/tests/validate-tests/pitivi.py
@@ -0,0 +1,35 @@
+# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python
+#
+# Copyright (c) 2014,Thibault Saunier <thibault saunier collabora com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+"""
+The GES GstValidate default testsuite
+"""
+import os
+
+
+TEST_MANAGER = "pitivi"
+
+
+def setup_tests(test_manager, options):
+ print("Setting up Pitivi default tests")
+ options.pitivi_scenario_paths = [os.path.abspath(os.path.join(os.path.dirname(__file__)))]
+ options.add_paths(os.path.abspath(os.path.join(os.path.dirname(__file__),
+ "..", "samples")))
+ test_manager.register_defaults()
+ return True
diff --git a/tests/validate-tests/pitivivalidate.py b/tests/validate-tests/pitivivalidate.py
new file mode 100644
index 0000000..5ffb5c6
--- /dev/null
+++ b/tests/validate-tests/pitivivalidate.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2013,Thibault Saunier <thibault saunier collabora com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import urlparse
+import utils
+from urllib import unquote
+from baseclasses import GstValidateTest, TestsManager, ScenarioManager
+
+Pitivi_DURATION_TOLERANCE = utils.GST_SECOND / 2
+
+PITIVI_COMMAND = "pitivi"
+if "win32" in sys.platform:
+ PITIVI_COMMAND += ".exe"
+
+
+def quote_uri(uri):
+ """
+ Encode a URI/path according to RFC 2396, without touching the file:/// part.
+ """
+ # Split off the "file:///" part, if present.
+ parts = urlparse.urlsplit(uri, allow_fragments=False)
+ # Make absolutely sure the string is unquoted before quoting again!
+ raw_path = unquote(parts.path)
+ return utils.path2url(raw_path)
+
+
+class PitiviTest(GstValidateTest):
+ def __init__(self, classname, options, reporter, scenario,
+ combination=None):
+
+ super(PitiviTest, self).__init__(PITIVI_COMMAND, classname, options, reporter,
+ scenario=None)
+ self._scenario = scenario
+
+ def set_sample_paths(self):
+ paths = self.options.paths
+
+ if not isinstance(paths, list):
+ paths = [paths]
+
+ for path in paths:
+ # We always want paths separator to be cut with '/' for ges-launch
+ path = path.replace("\\", "/")
+ self.add_arguments("--ges-sample-path-recurse", quote_uri(path))
+
+ def build_arguments(self):
+ GstValidateTest.build_arguments(self)
+
+ self.set_sample_paths()
+ self.add_arguments(self._scenario.path)
+
+
+class PitiviTestsManager(TestsManager):
+ name = "pitivi"
+
+ _scenarios = ScenarioManager()
+
+ def __init__(self):
+ super(PitiviTestsManager, self).__init__()
+
+ def init(self):
+ self.fixme("Implement init checking")
+
+ return True
+
+ def add_options(self, parser):
+ group = parser.add_argument_group("Pitivi specific option group"
+ " and behaviours",
+ description="")
+ group.add_argument("--pitivi-scenarios-paths", dest="pitivi_scenario_paths",
+ default=os.path.join(os.path.basename(__file__),
+ "pitivi",
+ "pitivi scenarios"),
+ help="Paths in which to look for scenario files")
+
+ def set_settings(self, options, args, reporter):
+ TestsManager.set_settings(self, options, args, reporter)
+ self._scenarios.config = self.options
+
+ try:
+ os.makedirs(utils.url2path(options.dest)[0])
+ except OSError:
+ pass
+
+ def list_tests(self):
+ return self.tests
+
+ def register_defaults(self):
+ scenarios = list()
+ for path in self.options.pitivi_scenario_paths:
+ for root, dirs, files in os.walk(path):
+ for f in files:
+ if not f.endswith(".scenario"):
+ continue
+ scenarios.append(os.path.join(path, root, f))
+
+ for scenario_name in scenarios:
+ scenario = self._scenarios.get_scenario(scenario_name)
+ if scenario is None:
+ continue
+
+ classname = "pitivi.%s" % (scenario.name)
+ self.add_test(PitiviTest(classname,
+ self.options,
+ self.reporter,
+ scenario=scenario)
+ )
diff --git a/tests/validate-tests/runtests b/tests/validate-tests/runtests
new file mode 100755
index 0000000..76e627f
--- /dev/null
+++ b/tests/validate-tests/runtests
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+GST_VALIDATE_LAUNCHER_COMMAND = "gst-validate-launcher"
+
+if (os.system(GST_VALIDATE_LAUNCHER_COMMAND + " -h > %s 2>&1" % os.devnull) != 0):
+ print("Make sure to install gst-validate:
http://cgit.freedesktop.org/gstreamer/gst-devtools/tree/validate/"
+ " before running the testsuite")
+ sys.exit(127)
+
+sys.exit(os.system("GST_VALIDATE_APPS_DIR=%s %s %s %s"
+ % (os.path.abspath(os.path.join(os.path.dirname(__file__))),
+ GST_VALIDATE_LAUNCHER_COMMAND,
+ os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__))), "pitivi.py"),
+ ' '.join(sys.argv[1:]))))
diff --git a/tests/validate-tests/select_clip.scenario b/tests/validate-tests/select_clip.scenario
new file mode 100644
index 0000000..253d6c2
--- /dev/null
+++ b/tests/validate-tests/select_clip.scenario
@@ -0,0 +1,18 @@
+description, seek=true, handles-states=true
+pause;
+add-asset,
id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip;
+add-layer, priority=(int)0;
+add-clip, name=(string)uriclip0, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip, start=(double)0, inpoint=(double)0, duration=(double)2;
+commit;
+add-clip, name=(string)uriclip1, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip, start=(double)2, inpoint=(double)0, duration=(double)2;
+commit;
+zoom-fit, not-mandatory-action-type=(boolean)true;
+select-clips, clip-name=uriclip1;
+wait, duration=0.5
+select-clips, clip-name=uriclip0;
+wait, duration=0.5
+select-clips, clip-name=uriclip1, mode=ctrl;
+wait, duration=0.5
+select-clips, clip-name=uriclip1, mode=ctrl, selection="uriclip0";
+wait, duration=0.5
+stop, force=true;
diff --git a/tests/validate-tests/simple_play.scenario b/tests/validate-tests/simple_play.scenario
new file mode 100644
index 0000000..c7d2586
--- /dev/null
+++ b/tests/validate-tests/simple_play.scenario
@@ -0,0 +1,13 @@
+description, seek=false, handles-states=true
+add-asset,
id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/tears_of_steel.webm,
type=(string)GESUriClip;
+add-layer, priority=(int)0;
+add-clip, name=(string)uriclip0, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/tears_of_steel.webm,
type=(string)GESUriClip, start=(double)0, inpoint=(double)0, duration=(double)2;
+commit;
+add-clip, name=(string)uriclip1, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/tears_of_steel.webm,
type=(string)GESUriClip, start=(double)2, inpoint=(double)0, duration=(double)2;
+set-control-source, binding-type=(string)direct, interpolation-mode=(string)linear,
element-name=(string)videourisource1, property-name=(string)alpha, source-type=(string)interpolation;
+set-control-source, binding-type=(string)direct, interpolation-mode=(string)linear,
element-name=(string)audiourisource1, property-name=(string)volume, source-type=(string)interpolation;
+commit;
+zoom-fit, optional-action-type=(boolean)true;
+set-zoom-level, optional-action-type=(boolean)true, level=(double)73;
+commit;
+stop, force=true;
diff --git a/tests/validate-tests/simple_ripple.scenario b/tests/validate-tests/simple_ripple.scenario
new file mode 100644
index 0000000..6f2d18a
--- /dev/null
+++ b/tests/validate-tests/simple_ripple.scenario
@@ -0,0 +1,126 @@
+description, seek=true, handles-states=true
+pause;
+add-asset,
id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip;
+add-layer, priority=(int)0;
+add-clip, name=(string)uriclip0, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip, start=(double)0, inpoint=(double)0, duration=(double)2;
+commit;
+add-clip, name=(string)uriclip1, layer-priority=(int)0,
asset-id=(string)file:///home/someone/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip, start=(double)2, inpoint=(double)0, duration=(double)2;
+commit;
+zoom-fit, not-mandatory-action-type=(boolean)true;
+set-zoom-level, not-mandatory-action-type=(boolean)true, level=(double)73;
+edit-container, position=(double)1.993988506, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.9914189330000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.9888493599999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.986279787, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.978571069, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.9246100399999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.875788156, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.855231574, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.832105418, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.798700972, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.7807139620000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.7498790879999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.7241833600000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.711335496, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.6907789129999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.659944039, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.6214004470000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.6008438650000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.5854264280000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.567439418, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.546882836, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.52375668, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.503200098, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.487782661, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.4620869329999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.4415303500000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.4029867579999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.3798606019999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.3618735930000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.343886583, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.3104821360000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.27707769, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.2462428160000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.2025600780000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.1665860589999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.1306120390000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.1151946020000001, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.0843597279999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)1.0483857089999999, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.99185510700000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.953311515, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.93532450499999997, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.87879390300000004, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.80427629099999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.76316312600000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.72975867999999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.68607594199999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.64496277700000004, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.61155833000000004, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.57301473800000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.54217986399999996, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52676242699999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.52162328099999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.50877541699999995, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.501066699, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.49078840800000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.48821883500000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.48307968899999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.48307968899999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.48307968899999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.48051011599999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.467662252, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.467662252, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.45481438800000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44710567000000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44453609700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44196652400000003, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44196652400000003, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.44196652400000003, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43939695099999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43939695099999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43682737799999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43682737799999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43425780600000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43425780600000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43425780600000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43425780600000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43168823299999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43168823299999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43168823299999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.43168823299999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42911865999999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42911865999999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42911865999999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42911865999999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42654908699999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.423979514, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.42140994199999998, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.41884036899999999, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.416270796, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.41370122300000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.41113165000000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.41113165000000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40856207700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40856207700000002, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.405992505, edge=(string)edge_end, container-name=(string)uriclip0,
new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40342293200000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40342293200000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40342293200000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+edit-container, position=(double)0.40342293200000001, edge=(string)edge_end,
container-name=(string)uriclip0, new-layer-priority=(int)-1, edit-mode=(string)edit_ripple;
+commit;
+stop, force=True;
diff --git a/tests/validate-tests/simple_split_clips.scenario
b/tests/validate-tests/simple_split_clips.scenario
new file mode 100644
index 0000000..5927477
--- /dev/null
+++ b/tests/validate-tests/simple_split_clips.scenario
@@ -0,0 +1,17 @@
+description, seek=true, handles-states=true
+pause;
+add-asset,
id=(string)file:///home/thiblahute/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_red.mkv,
type=(string)GESUriClip;
+add-asset,
id=(string)file:///home/thiblahute/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip;
+add-layer, priority=(int)0;
+add-clip, name=(string)uriclip0, layer-priority=(int)0,
asset-id=(string)file:///home/thiblahute/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_blue.webm,
type=(string)GESUriClip, start=(double)0, inpoint=(double)0, duration=(double)1.228;
+add-clip, name=(string)uriclip1, layer-priority=(int)0,
asset-id=(string)file:///home/thiblahute/devel/pitivi/1.0-uninstalled/pitivi/tests/samples/30fps_numeroted_frames_red.mkv,
type=(string)GESUriClip, start=(double)1.228, inpoint=(double)0, duration=(double)2;
+commit;
+zoom-fit, not-mandatory-action-type=(boolean)true;
+set-zoom-level, level=(double)69, not-mandatory-action-type=(boolean)true;
+seek, start=(double)0.62812454799999995, flags=(string)accurate+flush;
+split-clip, clip-name=(string)uriclip0, position=(double)0.62812454799999995;
+commit;
+seek, start=(double)2.0066820710000002, flags=(string)accurate+flush;
+split-clip, clip-name=(string)uriclip1, position=(double)2.0066820710000002;
+commit;
+stop, force=true;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]