"Fossies" - the Fresh Open Source Software Archive

Member "buildbot-2.5.1/buildbot/test/unit/test_schedulers_forcesched.py" (24 Nov 2019, 35649 Bytes) of package /linux/misc/buildbot-2.5.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_schedulers_forcesched.py": 2.5.0_vs_2.5.1.

    1 # This file is part of Buildbot.  Buildbot is free software: you can
    2 # redistribute it and/or modify it under the terms of the GNU General Public
    3 # License as published by the Free Software Foundation, version 2.
    4 #
    5 # This program is distributed in the hope that it will be useful, but WITHOUT
    6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    8 # details.
    9 #
   10 # You should have received a copy of the GNU General Public License along with
   11 # this program; if not, write to the Free Software Foundation, Inc., 51
   12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   13 #
   14 # Copyright Buildbot Team Members
   15 
   16 import json
   17 
   18 from twisted.internet import defer
   19 from twisted.trial import unittest
   20 
   21 from buildbot import config
   22 from buildbot.schedulers.forcesched import AnyPropertyParameter
   23 from buildbot.schedulers.forcesched import BaseParameter
   24 from buildbot.schedulers.forcesched import BooleanParameter
   25 from buildbot.schedulers.forcesched import ChoiceStringParameter
   26 from buildbot.schedulers.forcesched import CodebaseParameter
   27 from buildbot.schedulers.forcesched import CollectedValidationError
   28 from buildbot.schedulers.forcesched import FileParameter
   29 from buildbot.schedulers.forcesched import FixedParameter
   30 from buildbot.schedulers.forcesched import ForceScheduler
   31 from buildbot.schedulers.forcesched import IntParameter
   32 from buildbot.schedulers.forcesched import NestedParameter
   33 from buildbot.schedulers.forcesched import PatchParameter
   34 from buildbot.schedulers.forcesched import StringParameter
   35 from buildbot.schedulers.forcesched import UserNameParameter
   36 from buildbot.schedulers.forcesched import oneCodebase
   37 from buildbot.test.util import scheduler
   38 from buildbot.test.util.config import ConfigErrorsMixin
   39 from buildbot.test.util.misc import TestReactorMixin
   40 
   41 
   42 class TestForceScheduler(scheduler.SchedulerMixin, ConfigErrorsMixin,
   43                          TestReactorMixin, unittest.TestCase):
   44 
   45     OBJECTID = 19
   46     SCHEDULERID = 9
   47     maxDiff = None
   48 
   49     def setUp(self):
   50         self.setUpTestReactor()
   51         self.setUpScheduler()
   52 
   53     def tearDown(self):
   54         self.tearDownScheduler()
   55 
   56     def makeScheduler(self, name='testsched', builderNames=None,
   57                       **kw):
   58         if builderNames is None:
   59             builderNames = ['a', 'b']
   60         sched = self.attachScheduler(
   61             ForceScheduler(name=name, builderNames=builderNames, **kw),
   62             self.OBJECTID, self.SCHEDULERID,
   63             overrideBuildsetMethods=True,
   64             createBuilderDB=True)
   65         sched.master.config = config.MasterConfig()
   66 
   67         self.assertEqual(sched.name, name)
   68 
   69         return sched
   70 
   71     # tests
   72 
   73     def test_compare_branch(self):
   74         self.assertNotEqual(
   75             ForceScheduler(name="testched", builderNames=[]),
   76             ForceScheduler(
   77                 name="testched", builderNames=[],
   78                 codebases=oneCodebase(
   79                     branch=FixedParameter("branch", "fishing/pole"))))
   80 
   81     def test_compare_reason(self):
   82         self.assertNotEqual(
   83             ForceScheduler(name="testched", builderNames=[],
   84                            reason=FixedParameter("reason", "no fish for you!")),
   85             ForceScheduler(name="testched", builderNames=[],
   86                            reason=FixedParameter("reason", "thanks for the fish!")))
   87 
   88     def test_compare_revision(self):
   89         self.assertNotEqual(
   90             ForceScheduler(
   91                 name="testched", builderNames=[],
   92                 codebases=oneCodebase(
   93                     revision=FixedParameter("revision", "fish-v1"))),
   94             ForceScheduler(
   95                 name="testched", builderNames=[],
   96                 codebases=oneCodebase(
   97                     revision=FixedParameter("revision", "fish-v2"))))
   98 
   99     def test_compare_repository(self):
  100         self.assertNotEqual(
  101             ForceScheduler(
  102                 name="testched", builderNames=[],
  103                 codebases=oneCodebase(
  104                     repository=FixedParameter("repository", "git://pond.org/fisher.git"))),
  105             ForceScheduler(
  106                 name="testched", builderNames=[],
  107                 codebases=oneCodebase(
  108                     repository=FixedParameter("repository", "svn://ocean.com/trawler/"))))
  109 
  110     def test_compare_project(self):
  111         self.assertNotEqual(
  112             ForceScheduler(
  113                 name="testched", builderNames=[],
  114                 codebases=oneCodebase(
  115                     project=FixedParameter("project", "fisher"))),
  116             ForceScheduler(
  117                 name="testched", builderNames=[],
  118                 codebases=oneCodebase(
  119                     project=FixedParameter("project", "trawler"))))
  120 
  121     def test_compare_username(self):
  122         self.assertNotEqual(
  123             ForceScheduler(name="testched", builderNames=[]),
  124             ForceScheduler(name="testched", builderNames=[],
  125                            username=FixedParameter("username", "The Fisher King <avallach@atlantis.al>")))
  126 
  127     def test_compare_properties(self):
  128         self.assertNotEqual(
  129             ForceScheduler(name="testched", builderNames=[],
  130                            properties=[]),
  131             ForceScheduler(name="testched", builderNames=[],
  132                            properties=[FixedParameter("prop", "thanks for the fish!")]))
  133 
  134     def test_compare_codebases(self):
  135         self.assertNotEqual(
  136             ForceScheduler(name="testched", builderNames=[],
  137                            codebases=['bar']),
  138             ForceScheduler(name="testched", builderNames=[],
  139                            codebases=['foo']))
  140 
  141     @defer.inlineCallbacks
  142     def test_basicForce(self):
  143         sched = self.makeScheduler()
  144 
  145         res = yield sched.force('user', builderNames=['a'], branch='a', reason='because', revision='c',
  146                                 repository='d', project='p'
  147                                 )
  148 
  149         # only one builder forced, so there should only be one brid
  150         self.assertEqual(res, (500, {1000: 100}))
  151         self.assertEqual(self.addBuildsetCalls, [
  152             ('addBuildsetForSourceStampsWithDefaults', dict(
  153                 builderNames=['a'],
  154                 waited_for=False,
  155                 properties={
  156                     'owner': ('user', 'Force Build Form'),
  157                     'reason': ('because', 'Force Build Form'),
  158                 },
  159                 reason="A build was forced by 'user': because",
  160                 sourcestamps=[
  161                     {'codebase': '', 'branch': 'a', 'revision': 'c',
  162                      'repository': 'd', 'project': 'p'},
  163                 ])),
  164         ])
  165 
  166     @defer.inlineCallbacks
  167     def test_basicForce_reasonString(self):
  168         """Same as above, but with a reasonString"""
  169         sched = self.makeScheduler(
  170             reasonString='%(owner)s wants it %(reason)s')
  171 
  172         res = yield sched.force('user', builderNames=['a'], branch='a', reason='because', revision='c',
  173                                 repository='d', project='p'
  174                                 )
  175         bsid, brids = res
  176 
  177         # only one builder forced, so there should only be one brid
  178         self.assertEqual(len(brids), 1)
  179 
  180         self.assertEqual(self.addBuildsetCalls, [
  181             ('addBuildsetForSourceStampsWithDefaults', {
  182                 'builderNames': ['a'],
  183                 'properties': {'owner': ('user', 'Force Build Form'),
  184                                'reason': ('because', 'Force Build Form')},
  185                 'reason': 'user wants it because',
  186                 'sourcestamps': [{'branch': 'a',
  187                                   'codebase': '',
  188                                   'project': 'p',
  189                                   'repository': 'd',
  190                                   'revision': 'c'}],
  191                 'waited_for': False}),
  192         ])
  193         (bsid,
  194          dict(reason="user wants it because",
  195               brids=brids,
  196               external_idstring=None,
  197               properties=[('owner', ('user', 'Force Build Form')),
  198                           ('reason', ('because', 'Force Build Form')),
  199                           ('scheduler', ('testsched', 'Scheduler')),
  200                           ],
  201               sourcestampsetid=100),
  202          {'':
  203           dict(branch='a', revision='c', repository='d', codebase='',
  204                project='p', sourcestampsetid=100)
  205           })
  206 
  207     @defer.inlineCallbacks
  208     def test_force_allBuilders(self):
  209         sched = self.makeScheduler()
  210 
  211         res = yield sched.force('user', branch='a', reason='because', revision='c',
  212                                 repository='d', project='p',
  213                                 )
  214         self.assertEqual(res, (500, {1000: 100, 1001: 101}))
  215         self.assertEqual(self.addBuildsetCalls, [
  216             ('addBuildsetForSourceStampsWithDefaults', dict(
  217                 builderNames=['a', 'b'],
  218                 waited_for=False,
  219                 properties={
  220                     'owner': ('user', 'Force Build Form'),
  221                     'reason': ('because', 'Force Build Form'),
  222                 },
  223                 reason="A build was forced by 'user': because",
  224                 sourcestamps=[
  225                     {'codebase': '', 'branch': 'a', 'revision': 'c',
  226                      'repository': 'd', 'project': 'p'},
  227                 ])),
  228         ])
  229 
  230     @defer.inlineCallbacks
  231     def test_force_someBuilders(self):
  232         sched = self.makeScheduler(builderNames=['a', 'b', 'c'])
  233 
  234         res = yield sched.force('user', builderNames=['a', 'b'],
  235                                 branch='a', reason='because', revision='c',
  236                                 repository='d', project='p',
  237                                 )
  238         self.assertEqual(res, (500, {1000: 100, 1001: 101}))
  239         self.assertEqual(self.addBuildsetCalls, [
  240             ('addBuildsetForSourceStampsWithDefaults', dict(
  241                 builderNames=['a', 'b'],
  242                 waited_for=False,
  243                 properties={
  244                     'owner': ('user', 'Force Build Form'),
  245                     'reason': ('because', 'Force Build Form'),
  246                 },
  247                 reason="A build was forced by 'user': because",
  248                 sourcestamps=[
  249                     {'codebase': '', 'branch': 'a', 'revision': 'c',
  250                      'repository': 'd', 'project': 'p'},
  251                 ])),
  252         ])
  253 
  254     def test_bad_codebases(self):
  255 
  256         # codebases must be a list of either string or BaseParameter types
  257         with self.assertRaisesConfigError(
  258                 "ForceScheduler 'foo': 'codebases' must be a "
  259                 "list of strings or CodebaseParameter objects:"):
  260             ForceScheduler(name='foo', builderNames=['bar'], codebases=[123],)
  261 
  262         with self.assertRaisesConfigError(
  263                 "ForceScheduler 'foo': 'codebases' must be a "
  264                 "list of strings or CodebaseParameter objects:"):
  265             ForceScheduler(name='foo', builderNames=['bar'],
  266                            codebases=[IntParameter('foo')])
  267 
  268         # codebases cannot be empty
  269         with self.assertRaisesConfigError(
  270                 "ForceScheduler 'foo': 'codebases' cannot be "
  271                 "empty; use [CodebaseParameter(codebase='', hide=True)] if needed:"):
  272             ForceScheduler(name='foo', builderNames=['bar'], codebases=[])
  273 
  274         # codebases cannot be a dictionary
  275         # dictType on Python 3 is: "<class 'dict'>"
  276         # dictType on Python 2 is: "<type 'dict'>"
  277         dictType = str(type({}))
  278         errMsg = ("ForceScheduler 'foo': 'codebases' should be a list "
  279                   "of strings or CodebaseParameter, "
  280                   "not {}".format(dictType))
  281         with self.assertRaisesConfigError(errMsg):
  282             ForceScheduler(name='foo', builderNames=['bar'],
  283                            codebases={'cb': {'branch': 'trunk'}})
  284 
  285     @defer.inlineCallbacks
  286     def test_good_codebases(self):
  287         sched = self.makeScheduler(codebases=['foo', CodebaseParameter('bar')])
  288         res = yield sched.force('user', builderNames=['a'], reason='because',
  289                                 foo_branch='a', foo_revision='c', foo_repository='d', foo_project='p',
  290                                 bar_branch='a2', bar_revision='c2', bar_repository='d2', bar_project='p2'
  291                                 )
  292 
  293         bsid, brids = res
  294         expProperties = {
  295             'owner': ('user', 'Force Build Form'),
  296             'reason': ('because', 'Force Build Form'),
  297         }
  298         self.assertEqual(self.addBuildsetCalls, [
  299             ('addBuildsetForSourceStampsWithDefaults', dict(
  300                 builderNames=['a'],
  301                 waited_for=False,
  302                 properties=expProperties,
  303                 reason="A build was forced by 'user': because",
  304                 sourcestamps=[
  305                     {'branch': 'a2', 'project': 'p2', 'repository': 'd2',
  306                         'revision': 'c2', 'codebase': 'bar'},
  307                     {'branch': 'a', 'project': 'p', 'repository': 'd',
  308                         'revision': 'c', 'codebase': 'foo'},
  309                 ])),
  310         ])
  311 
  312     @defer.inlineCallbacks
  313     def test_codebase_with_patch(self):
  314         sched = self.makeScheduler(codebases=['foo', CodebaseParameter('bar', patch=PatchParameter())])
  315         res = yield sched.force('user', builderNames=['a'], reason='because',
  316                                 foo_branch='a', foo_revision='c', foo_repository='d', foo_project='p',
  317                                 bar_branch='a2', bar_revision='c2', bar_repository='d2', bar_project='p2', bar_patch_body="xxx"
  318                                 )
  319 
  320         bsid, brids = res
  321         expProperties = {
  322             'owner': ('user', 'Force Build Form'),
  323             'reason': ('because', 'Force Build Form'),
  324         }
  325 
  326         self.assertEqual(self.addBuildsetCalls, [
  327             ('addBuildsetForSourceStampsWithDefaults', dict(
  328                 builderNames=['a'],
  329                 waited_for=False,
  330                 properties=expProperties,
  331                 reason="A build was forced by 'user': because",
  332                 sourcestamps=[
  333                     {'branch': 'a2', 'project': 'p2', 'repository': 'd2',
  334                         'revision': 'c2', 'codebase': 'bar',
  335                         'patch_body': 'xxx', 'patch_author': '', 'patch_subdir': '.',
  336                         'patch_comment': '', 'patch_level': 1},
  337                     {'branch': 'a', 'project': 'p', 'repository': 'd',
  338                         'revision': 'c', 'codebase': 'foo'},
  339                 ])),
  340         ])
  341 
  342     def formatJsonForTest(self, gotJson):
  343         ret = ""
  344         linestart = "expectJson='"
  345         spaces = 7 * 4 + 2
  346         while len(gotJson) > (90 - spaces):
  347             gotJson = " " * spaces + linestart + gotJson
  348             pos = gotJson[:100].rfind(",")
  349             if pos > 0:
  350                 pos += 2
  351             ret += gotJson[:pos] + "'\n"
  352             gotJson = gotJson[pos:]
  353             linestart = "'"
  354         ret += " " * spaces + linestart + gotJson + "')\n"
  355         return ret
  356 
  357     # value = the value to be sent with the parameter (ignored if req is set)
  358     # expect = the expected result (can be an exception type)
  359     # klass = the parameter class type
  360     # req = use this request instead of the auto-generated one based on value
  361     @defer.inlineCallbacks
  362     def do_ParameterTest(self,
  363                          expect,
  364                          klass,
  365                          # None=one prop, Exception=exception, dict=many props
  366                          expectKind=None,
  367                          owner='user',
  368                          value=None, req=None,
  369                          expectJson=None,
  370                          **kwargs):
  371 
  372         name = kwargs.setdefault('name', 'p1')
  373 
  374         # construct one if needed
  375         if isinstance(klass, type):
  376             prop = klass(**kwargs)
  377         else:
  378             prop = klass
  379 
  380         self.assertEqual(prop.name, name)
  381         self.assertEqual(prop.label, kwargs.get('label', prop.name))
  382         if expectJson is not None:
  383             gotSpec = prop.getSpec()
  384             gotJson = json.dumps(gotSpec)
  385             expectSpec = json.loads(expectJson)
  386             if gotSpec != expectSpec:
  387                 try:
  388                     import xerox  # pylint: disable=import-outside-toplevel
  389                     formatted = self.formatJsonForTest(gotJson)
  390                     print(
  391                         "You may update the test with (copied to clipboard):\n" + formatted)
  392                     xerox.copy(formatted)
  393                     input()
  394                 except ImportError:
  395                     print("Note: for quick fix, pip install xerox")
  396             self.assertEqual(gotSpec, expectSpec)
  397 
  398         sched = self.makeScheduler(properties=[prop])
  399 
  400         if not req:
  401             req = {name: value, 'reason': 'because'}
  402         try:
  403             bsid, brids = yield sched.force(owner, builderNames=['a'], **req)
  404         except Exception as e:
  405             if expectKind is not Exception:
  406                 # an exception is not expected
  407                 raise
  408             if not isinstance(e, expect):
  409                 # the exception is the wrong kind
  410                 raise
  411             return None  # success
  412 
  413         expect_props = {
  414             'owner': ('user', 'Force Build Form'),
  415             'reason': ('because', 'Force Build Form'),
  416         }
  417 
  418         if expectKind is None:
  419             expect_props[name] = (expect, 'Force Build Form')
  420         elif expectKind is dict:
  421             for k, v in expect.items():
  422                 expect_props[k] = (v, 'Force Build Form')
  423         else:
  424             self.fail("expectKind is wrong type!")
  425 
  426         # only forced on 'a'
  427         self.assertEqual((bsid, brids), (500, {1000: 100}))
  428         self.assertEqual(self.addBuildsetCalls, [
  429             ('addBuildsetForSourceStampsWithDefaults', dict(
  430                 builderNames=['a'],
  431                 waited_for=False,
  432                 properties=expect_props,
  433                 reason="A build was forced by 'user': because",
  434                 sourcestamps=[
  435                     {'branch': '', 'project': '', 'repository': '',
  436                      'revision': '', 'codebase': ''},
  437                 ])),
  438         ])
  439 
  440     def test_StringParameter(self):
  441         self.do_ParameterTest(value="testedvalue", expect="testedvalue",
  442                               klass=StringParameter,
  443                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  444                               '"tablabel": "p1", "type": "text", "default": "", "required": false, '
  445                               '"multiple": false, "regex": null, "hide": false, "maxsize": null, '
  446                               '"size": 10, "autopopulate": null}')
  447 
  448     def test_StringParameter_Required(self):
  449         self.do_ParameterTest(value=" ", expect=CollectedValidationError,
  450                               expectKind=Exception,
  451                               klass=StringParameter, required=True)
  452 
  453     def test_StringParameter_maxsize(self):
  454         self.do_ParameterTest(value="xx" * 20, expect=CollectedValidationError,
  455                               expectKind=Exception,
  456                               klass=StringParameter, maxsize=10)
  457 
  458     def test_FileParameter_maxsize(self):
  459         self.do_ParameterTest(value="xx" * 20, expect=CollectedValidationError,
  460                               expectKind=Exception,
  461                               klass=FileParameter, maxsize=10)
  462 
  463     def test_FileParameter(self):
  464         self.do_ParameterTest(value="xx", expect="xx",
  465                               klass=FileParameter,
  466                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  467                               '"tablabel": "p1", "type": "file", "default": "", "required": false, '
  468                               '"multiple": false, "regex": null, "hide": false, '
  469                               '"maxsize": 10485760, "autopopulate": null}')
  470 
  471     def test_PatchParameter(self):
  472         self.do_ParameterTest(req=dict(p1_author='me', reason="because"), expect={
  473                                    'author': 'me',
  474                                    'body': '',
  475                                    'comment': '',
  476                                    'level': 1,
  477                                    'subdir': '.'},
  478                               klass=PatchParameter,
  479                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", "autopopulate": null, '
  480                               '"tablabel": "p1", "type": "nested", "default": "", "required": false, '
  481                               '"multiple": false, "regex": null, "hide": false, "maxsize": null, '
  482                               '"layout": "vertical", "columns": 1, "fields": [{"name": "body", '
  483                               '"fullName": "p1_body", "label": "body", "tablabel": "body", "autopopulate": null, '
  484                               '"type": "file", "default": "", "required": false, "multiple": false, '
  485                               '"regex": null, "hide": false, "maxsize": 10485760}, {"name": "level", '
  486                               '"fullName": "p1_level", "label": "level", "tablabel": "level", '
  487                               '"type": "int", "default": 1, "required": false, "multiple": false, '
  488                               '"regex": null, "hide": false, "maxsize": null, "size": 10, "autopopulate": null}, '
  489                               '{"name": "author", "fullName": "p1_author", "label": "author", '
  490                               '"tablabel": "author", "type": "text", "default": "", "autopopulate": null, '
  491                               '"required": false, "multiple": false, "regex": null, "hide": false, '
  492                               '"maxsize": null, "size": 10}, {"name": "comment", "autopopulate": null, '
  493                               '"fullName": "p1_comment", "label": "comment", "tablabel": "comment", '
  494                               '"type": "text", "default": "", "required": false, "multiple": false, '
  495                               '"regex": null, "hide": false, "maxsize": null, "size": 10}, '
  496                               '{"name": "subdir", "fullName": "p1_subdir", "label": "subdir", '
  497                               '"tablabel": "subdir", "type": "text", "default": ".", "autopopulate": null, '
  498                               '"required": false, "multiple": false, "regex": null, "hide": false, '
  499                               '"maxsize": null, "size": 10}]}')
  500 
  501     def test_IntParameter(self):
  502         self.do_ParameterTest(value="123", expect=123, klass=IntParameter,
  503                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  504                               '"tablabel": "p1", "type": "int", "default": 0, "required": false, '
  505                               '"multiple": false, "regex": null, "hide": false, "maxsize": null, '
  506                               '"size": 10, "autopopulate": null}')
  507 
  508     def test_FixedParameter(self):
  509         self.do_ParameterTest(value="123", expect="321", klass=FixedParameter,
  510                               default="321",
  511                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  512                               '"tablabel": "p1", "type": "fixed", "default": "321", '
  513                               '"required": false, "multiple": false, "regex": null, "hide": true, '
  514                               '"maxsize": null, "autopopulate": null}')
  515 
  516     def test_BooleanParameter_True(self):
  517         req = dict(p1=True, reason='because')
  518         self.do_ParameterTest(value="123", expect=True, klass=BooleanParameter,
  519                               req=req,
  520                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  521                               '"tablabel": "p1", "type": "bool", "default": "", "required": false, '
  522                               '"multiple": false, "regex": null, "hide": false, '
  523                               '"maxsize": null, "autopopulate": null}')
  524 
  525     def test_BooleanParameter_False(self):
  526         req = dict(p2=True, reason='because')
  527         self.do_ParameterTest(value="123", expect=False,
  528                               klass=BooleanParameter, req=req)
  529 
  530     def test_UserNameParameter(self):
  531         email = "test <test@buildbot.net>"
  532         self.do_ParameterTest(value=email, expect=email,
  533                               klass=UserNameParameter(),
  534                               name="username", label="Your name:",
  535                               expectJson='{"name": "username", "fullName": "username", '
  536                               '"label": "Your name:", "tablabel": "Your name:", "type": "username", '
  537                               '"default": "", "required": false, "multiple": false, "regex": null, '
  538                               '"hide": false, "maxsize": null, "size": 30, '
  539                               '"need_email": true, "autopopulate": null}')
  540 
  541     def test_UserNameParameterIsValidMail(self):
  542         email = "test@buildbot.net"
  543         self.do_ParameterTest(value=email, expect=email,
  544                               klass=UserNameParameter(),
  545                               name="username", label="Your name:",
  546                               expectJson='{"name": "username", "fullName": "username", '
  547                               '"label": "Your name:", "tablabel": "Your name:", "type": "username", '
  548                               '"default": "", "required": false, "multiple": false, "regex": null, '
  549                               '"hide": false, "maxsize": null, "size": 30, '
  550                               '"need_email": true, "autopopulate": null}')
  551 
  552     def test_UserNameParameterIsValidMailBis(self):
  553         email = "<test@buildbot.net>"
  554         self.do_ParameterTest(value=email, expect=email,
  555                               klass=UserNameParameter(),
  556                               name="username", label="Your name:",
  557                               expectJson='{"name": "username", "fullName": "username", '
  558                               '"label": "Your name:", "tablabel": "Your name:", "type": "username", '
  559                               '"default": "", "required": false, "multiple": false, "regex": null, '
  560                               '"hide": false, "maxsize": null, "size": 30, '
  561                               '"need_email": true, "autopopulate": null}')
  562 
  563     def test_ChoiceParameter(self):
  564         self.do_ParameterTest(value='t1', expect='t1',
  565                               klass=ChoiceStringParameter, choices=[
  566                                   't1', 't2'],
  567                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  568                               '"tablabel": "p1", "type": "list", "default": "", "required": false, '
  569                               '"multiple": false, "regex": null, "hide": false, "maxsize": null, '
  570                               '"choices": ["t1", "t2"], "strict": true, "autopopulate": null}')
  571 
  572     def test_ChoiceParameterError(self):
  573         self.do_ParameterTest(value='t3',
  574                               expect=CollectedValidationError,
  575                               expectKind=Exception,
  576                               klass=ChoiceStringParameter, choices=[
  577                                   't1', 't2'],
  578                               debug=False)
  579 
  580     def test_ChoiceParameterError_notStrict(self):
  581         self.do_ParameterTest(value='t1', expect='t1',
  582                               strict=False,
  583                               klass=ChoiceStringParameter, choices=['t1', 't2'])
  584 
  585     def test_ChoiceParameterMultiple(self):
  586         self.do_ParameterTest(value=['t1', 't2'], expect=['t1', 't2'],
  587                               klass=ChoiceStringParameter, choices=['t1', 't2'], multiple=True,
  588                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", '
  589                               '"tablabel": "p1", "type": "list", "default": "", "required": false, '
  590                               '"multiple": true, "regex": null, "hide": false, "maxsize": null, '
  591                               '"choices": ["t1", "t2"], "strict": true, "autopopulate": null}')
  592 
  593     def test_ChoiceParameterMultipleError(self):
  594         self.do_ParameterTest(value=['t1', 't3'],
  595                               expect=CollectedValidationError,
  596                               expectKind=Exception,
  597                               klass=ChoiceStringParameter, choices=[
  598                                   't1', 't2'],
  599                               multiple=True, debug=False)
  600 
  601     def test_NestedParameter(self):
  602         fields = [
  603             IntParameter(name="foo")
  604         ]
  605         self.do_ParameterTest(req=dict(p1_foo='123', reason="because"),
  606                               expect=dict(foo=123),
  607                               klass=NestedParameter, fields=fields,
  608                               expectJson='{"name": "p1", "fullName": "p1", "label": "p1", "autopopulate": null, '
  609                               '"tablabel": "p1", "type": "nested", "default": "", "required": false, '
  610                               '"multiple": false, "regex": null, "hide": false, "maxsize": null, '
  611                               '"layout": "vertical", "columns": 1, "fields": [{"name": "foo", '
  612                               '"fullName": "p1_foo", "label": "foo", "tablabel": "foo", "autopopulate": null, '
  613                               '"type": "int", "default": 0, "required": false, "multiple": false, '
  614                               '"regex": null, "hide": false, "maxsize": null, "size": 10}]}')
  615 
  616     def test_NestedNestedParameter(self):
  617         fields = [
  618             NestedParameter(name="inner", fields=[
  619                 StringParameter(name='str'),
  620                 AnyPropertyParameter(name='any')
  621             ]),
  622             IntParameter(name="foo")
  623         ]
  624         self.do_ParameterTest(req=dict(p1_foo='123',
  625                                        p1_inner_str="bar",
  626                                        p1_inner_any_name="hello",
  627                                        p1_inner_any_value="world",
  628                                        reason="because"),
  629                               expect=dict(
  630                                   foo=123, inner=dict(str="bar", hello="world")),
  631                               klass=NestedParameter, fields=fields)
  632 
  633     def test_NestedParameter_nullname(self):
  634         # same as above except "p1" and "any" are skipped
  635         fields = [
  636             NestedParameter(name="inner", fields=[
  637                 StringParameter(name='str'),
  638                 AnyPropertyParameter(name='')
  639             ]),
  640             IntParameter(name="foo"),
  641             NestedParameter(name='bar', fields=[
  642                 NestedParameter(
  643                     name='', fields=[AnyPropertyParameter(name='a')]),
  644                 NestedParameter(
  645                     name='', fields=[AnyPropertyParameter(name='b')])
  646             ])
  647         ]
  648         self.do_ParameterTest(req=dict(foo='123',
  649                                        inner_str="bar",
  650                                        inner_name="hello",
  651                                        inner_value="world",
  652                                        reason="because",
  653                                        bar_a_name="a",
  654                                        bar_a_value="7",
  655                                        bar_b_name="b",
  656                                        bar_b_value="8"),
  657                               expect=dict(foo=123,
  658                                           inner=dict(str="bar", hello="world"),
  659                                           bar={'a': '7', 'b': '8'}),
  660                               expectKind=dict,
  661                               klass=NestedParameter, fields=fields, name='')
  662 
  663     def test_bad_reason(self):
  664         with self.assertRaisesConfigError(
  665                 "ForceScheduler 'testsched': reason must be a StringParameter"):
  666             ForceScheduler(name='testsched', builderNames=[],
  667                            codebases=['bar'], reason="foo")
  668 
  669     def test_bad_username(self):
  670         with self.assertRaisesConfigError(
  671                 "ForceScheduler 'testsched': username must be a StringParameter"):
  672             ForceScheduler(name='testsched', builderNames=[],
  673                            codebases=['bar'], username="foo")
  674 
  675     def test_notstring_name(self):
  676         with self.assertRaisesConfigError(
  677                 "ForceScheduler name must be a unicode string:"):
  678             ForceScheduler(name=1234, builderNames=[], codebases=['bar'],
  679                            username="foo")
  680 
  681     def test_notidentifier_name(self):
  682         # FIXME: this test should be removed eventually when bug 3460 gets a
  683         # real fix
  684         with self.assertRaisesConfigError(
  685                 "ForceScheduler name must be an identifier: 'my scheduler'"):
  686             ForceScheduler(name='my scheduler', builderNames=[],
  687                            codebases=['bar'], username="foo")
  688 
  689     def test_emptystring_name(self):
  690         with self.assertRaisesConfigError(
  691                 "ForceScheduler name must not be empty:"):
  692             ForceScheduler(name='', builderNames=[], codebases=['bar'],
  693                            username="foo")
  694 
  695     def test_integer_builderNames(self):
  696         with self.assertRaisesConfigError(
  697                 "ForceScheduler 'testsched': builderNames must be a list of strings:"):
  698             ForceScheduler(name='testsched', builderNames=1234,
  699                            codebases=['bar'], username="foo")
  700 
  701     def test_listofints_builderNames(self):
  702         with self.assertRaisesConfigError(
  703                 "ForceScheduler 'testsched': builderNames must be a list of strings:"):
  704             ForceScheduler(name='testsched', builderNames=[1234],
  705                            codebases=['bar'], username="foo")
  706 
  707     def test_listofunicode_builderNames(self):
  708         ForceScheduler(name='testsched', builderNames=['a', 'b'])
  709 
  710     def test_listofmixed_builderNames(self):
  711         with self.assertRaisesConfigError(
  712                 "ForceScheduler 'testsched': builderNames must be a list of strings:"):
  713             ForceScheduler(name='testsched', builderNames=['test', 1234],
  714                            codebases=['bar'], username="foo")
  715 
  716     def test_integer_properties(self):
  717         with self.assertRaisesConfigError(
  718                 "ForceScheduler 'testsched': properties must be a list of BaseParameters:"):
  719             ForceScheduler(name='testsched', builderNames=[],
  720                            codebases=['bar'], username="foo",
  721                            properties=1234)
  722 
  723     def test_listofints_properties(self):
  724         with self.assertRaisesConfigError(
  725                 "ForceScheduler 'testsched': properties must be a list of BaseParameters:"):
  726             ForceScheduler(name='testsched', builderNames=[],
  727                            codebases=['bar'], username="foo",
  728                            properties=[1234, 2345])
  729 
  730     def test_listofmixed_properties(self):
  731         with self.assertRaisesConfigError(
  732                 "ForceScheduler 'testsched': properties must be a list of BaseParameters:"):
  733             ForceScheduler(name='testsched', builderNames=[],
  734                            codebases=['bar'], username="foo",
  735                            properties=[BaseParameter(name="test",),
  736                            4567])
  737 
  738     def test_novalue_to_parameter(self):
  739         with self.assertRaisesConfigError(
  740                 "Use default='1234' instead of value=... to give a default Parameter value"):
  741             BaseParameter(name="test", value="1234")