diff --git a/gui/mozregui/ui/nightlies.ui b/gui/mozregui/ui/nightlies.ui
index 874b3d13e..b59b13c3e 100644
--- a/gui/mozregui/ui/nightlies.ui
+++ b/gui/mozregui/ui/nightlies.ui
@@ -26,13 +26,6 @@
- -
-
-
- yyyy-MM-dd
-
-
-
-
@@ -40,13 +33,6 @@
- -
-
-
- yyyy-MM-dd
-
-
-
-
@@ -67,10 +53,24 @@
+ -
+
+
+ -
+
+
+
+
+ NightlyInputSelection
+ QWidget
+
+ 1
+
+
diff --git a/gui/mozregui/utils.py b/gui/mozregui/utils.py
index c18b5bae9..222984a71 100644
--- a/gui/mozregui/utils.py
+++ b/gui/mozregui/utils.py
@@ -1,6 +1,11 @@
from PyQt4.QtCore import QDir
from PyQt4.QtGui import QLineEdit, QPushButton, QWidget, QHBoxLayout, \
- QFileDialog, QFileSystemModel, QCompleter
+ QFileDialog, QFileSystemModel, QCompleter, QComboBox, QDateEdit, \
+ QStackedWidget
+
+from mozregression.releases import date_of_release, releases
+from mozregression.dates import parse_date
+from mozregression.errors import DateFormatError
class FSLineEdit(QLineEdit):
@@ -44,3 +49,45 @@ def browse_dialog(self):
)
if path:
self.line_edit.setPath(path)
+
+
+class NightlyInputSelection(QWidget):
+ """
+ Allow to select a date, a build id or a release number.
+ """
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
+ layout = QHBoxLayout(self)
+ self._create_widgets()
+ layout.addWidget(self.stacked)
+ layout.addWidget(self.select_combo)
+ self.setLayout(layout)
+
+ def _create_widgets(self):
+ self.stacked = QStackedWidget()
+ self.datew = QDateEdit()
+ self.datew.setDisplayFormat("yyyy-MM-dd")
+ self.stacked.addWidget(self.datew)
+ self.buildidw = QLineEdit()
+ self.stacked.addWidget(self.buildidw)
+ self.releasew = QComboBox()
+ self.releasew.addItems([str(k) for k in sorted(releases())])
+ self.stacked.addWidget(self.releasew)
+
+ self.select_combo = QComboBox()
+ self.select_combo.addItems(['date', 'buildid', 'release'])
+ self.select_combo.activated.connect(self.stacked.setCurrentIndex)
+
+ def get_date(self):
+ currentw = self.stacked.currentWidget()
+ if currentw == self.datew:
+ return self.datew.date().toPyDate()
+ elif currentw == self.buildidw:
+ buildid = unicode(self.buildidw.text())
+ try:
+ return parse_date(buildid)
+ except DateFormatError:
+ raise DateFormatError(buildid, "Not a valid build id: `%s`")
+ elif currentw == self.releasew:
+ return parse_date(
+ date_of_release(str(self.releasew.currentText())))
diff --git a/gui/mozregui/wizard.py b/gui/mozregui/wizard.py
index fe0f5256e..b84825e88 100644
--- a/gui/mozregui/wizard.py
+++ b/gui/mozregui/wizard.py
@@ -1,6 +1,7 @@
import mozinfo
+import datetime
from PyQt4.QtGui import QWizard, QWizardPage, QStringListModel, QMessageBox
-from PyQt4.QtCore import QString, QDate, QDateTime
+from PyQt4.QtCore import QString, QDateTime
from ui.intro import Ui_Intro
from ui.nightlies import Ui_Nightlies
@@ -9,17 +10,8 @@
from mozregression.fetch_configs import create_config, REGISTRY
from mozregression.launchers import REGISTRY as LAUNCHER_REGISTRY
-from mozregression.errors import LauncherNotRunnable
-
-
-def get_all_subclasses(cls):
- all_subclasses = []
-
- for subclass in cls.__subclasses__():
- all_subclasses.append(subclass)
- all_subclasses.extend(get_all_subclasses(subclass))
-
- return all_subclasses
+from mozregression.errors import LauncherNotRunnable, DateFormatError
+from mozregression.dates import to_datetime
def resolve_obj_name(obj, name):
@@ -144,20 +136,29 @@ class NightliesPage(WizardSelectionRangePage):
UI_CLASS = Ui_Nightlies
TITLE = "Date range selection"
SUBTITLE = ("Select the nightlies date range.")
- FIELDS = {"start_date": "start_date", "end_date": "end_date",
- "repository": "repository"}
+ FIELDS = {"repository": "repository"}
ID = 1
def __init__(self):
WizardPage.__init__(self)
now = QDateTime.currentDateTime()
- self.ui.start_date.setDateTime(now.addYears(-1))
- self.ui.end_date.setDateTime(now)
+ self.ui.start_date.datew.setDateTime(now.addYears(-1))
+ self.ui.end_date.datew.setDateTime(now)
+
+ def get_start_date(self):
+ return self.ui.start_date.get_date()
+
+ def get_end_date(self):
+ return self.ui.end_date.get_date()
def validatePage(self):
- start_date = self.ui.start_date.date()
- end_date = self.ui.end_date.date()
- current = QDateTime.currentDateTime().date()
+ try:
+ start_date = to_datetime(self.get_start_date())
+ end_date = to_datetime(self.get_end_date())
+ except DateFormatError as exc:
+ QMessageBox.critical(self, "Error", unicode(exc))
+ return False
+ current = datetime.datetime.now()
if start_date < end_date:
if end_date <= current:
return True
@@ -231,8 +232,6 @@ def __init__(self, parent=None):
# associate current text to comboboxes fields instead of current index
self.setDefaultProperty("QComboBox", "currentText",
"currentIndexChanged")
- # store QDate instead of QDateTime
- self.setDefaultProperty("QDateEdit", "date", "dateChanged")
self.addPage(IntroPage())
self.addPage(NightliesPage())
@@ -241,17 +240,19 @@ def __init__(self, parent=None):
def options(self):
options = {}
- for wizard_class in get_all_subclasses(WizardPage):
+ for page_id in self.pageIds():
+ wizard_class = self.page(page_id).__class__
for fieldname in wizard_class.FIELDS:
value = self.field(fieldname).toPyObject()
if isinstance(value, QString):
value = unicode(value)
- elif isinstance(value, QDate):
- value = value.toPyDate()
options[fieldname] = value
fetch_config = self.page(IntroPage.ID).fetch_config
if options['bisect_type'] == 'nightlies':
kind = "date"
+ nightlies_page = self.page(NightliesPage.ID)
+ options['start_date'] = nightlies_page.get_start_date()
+ options['end_date'] = nightlies_page.get_end_date()
fetch_config.set_nightly_repo(options['repository'])
else:
kind = "changeset"
diff --git a/gui/tests/test_utils.py b/gui/tests/test_utils.py
new file mode 100644
index 000000000..15ccab111
--- /dev/null
+++ b/gui/tests/test_utils.py
@@ -0,0 +1,49 @@
+import pytest
+import datetime
+
+from mozregui.utils import NightlyInputSelection
+from mozregression.errors import DateFormatError
+
+
+@pytest.fixture
+def pref_editor(qtbot):
+ widget = NightlyInputSelection()
+ qtbot.addWidget(widget)
+ widget.show()
+ qtbot.waitForWindowShown(widget)
+ return widget
+
+
+def test_date_widget_is_visible_by_default(pref_editor):
+ assert pref_editor.select_combo.currentText() == 'date'
+ assert pref_editor.datew.isVisible()
+ assert not pref_editor.buildidw.isVisible()
+ assert not pref_editor.releasew.isVisible()
+
+
+def test_switch_to_release_widget(pref_editor, qtbot):
+ qtbot.keyClicks(pref_editor.select_combo, "release")
+ assert pref_editor.select_combo.currentText() == 'release'
+ assert not pref_editor.datew.isVisible()
+ assert not pref_editor.buildidw.isVisible()
+ assert pref_editor.releasew.isVisible()
+
+
+@pytest.mark.parametrize("widname,value,expected", [
+ ("date", "20000101", datetime.date(2000, 1, 1)),
+ ("buildid", "20150102101112",
+ datetime.datetime(2015, 1, 2, 10, 11, 12)),
+ ("release", "40", datetime.date(2015, 5, 11)),
+])
+def test_get_date(pref_editor, qtbot, widname, value, expected):
+ qtbot.keyClicks(pref_editor.select_combo, widname)
+ qtbot.keyClicks(pref_editor.stacked.currentWidget(), value)
+ assert pref_editor.get_date() == expected
+
+
+def test_get_invalid_buildid(pref_editor, qtbot):
+ qtbot.keyClicks(pref_editor.select_combo, "buildid")
+ qtbot.keyClicks(pref_editor.stacked.currentWidget(), "12345")
+ with pytest.raises(DateFormatError) as ctx:
+ pref_editor.get_date()
+ assert 'build id' in str(ctx.value)
diff --git a/mozregression/dates.py b/mozregression/dates.py
index 4bf2f1db3..5027b7acd 100644
--- a/mozregression/dates.py
+++ b/mozregression/dates.py
@@ -17,7 +17,7 @@ def parse_date(date_string):
try:
return datetime.datetime.strptime(date_string, "%Y%m%d%H%M%S")
except ValueError:
- raise DateFormatError(date_string)
+ raise DateFormatError(date_string, "Not a valid build id: `%s`")
regex = re.compile(r'(\d{4})\-(\d{1,2})\-(\d{1,2})')
matched = regex.match(date_string)
if not matched:
diff --git a/mozregression/errors.py b/mozregression/errors.py
index 11063db8c..e9b538e1b 100644
--- a/mozregression/errors.py
+++ b/mozregression/errors.py
@@ -35,10 +35,8 @@ class DateFormatError(MozRegressionError):
"""
Raised when a date can not be parsed from a string.
"""
- def __init__(self, date_string):
- MozRegressionError.__init__(self,
- "Incorrect date format: `%s`"
- % date_string)
+ def __init__(self, date_string, format="Incorrect date format: `%s`"):
+ MozRegressionError.__init__(self, format % date_string)
class DownloadError(MozRegressionError):