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
17import weakref
18
19import mock
20
21from twisted.internet import defer
22from twisted.internet import reactor
23
24from buildbot import config
25from buildbot.data.graphql import GraphQLConnector
26from buildbot.test import fakedb
27from buildbot.test.fake import bworkermanager
28from buildbot.test.fake import endpoint
29from buildbot.test.fake import fakedata
30from buildbot.test.fake import fakemq
31from buildbot.test.fake import pbmanager
32from buildbot.test.fake.botmaster import FakeBotMaster
33from buildbot.test.fake.machine import FakeMachineManager
34from buildbot.util import service
35
36
37class FakeCache:
38
39    """Emulate an L{AsyncLRUCache}, but without any real caching.  This
40    I{does} do the weakref part, to catch un-weakref-able objects."""
41
42    def __init__(self, name, miss_fn):
43        self.name = name
44        self.miss_fn = miss_fn
45
46    def get(self, key, **kwargs):
47        d = self.miss_fn(key, **kwargs)
48
49        @d.addCallback
50        def mkref(x):
51            if x is not None:
52                weakref.ref(x)
53            return x
54        return d
55
56    def put(self, key, val):
57        pass
58
59
60class FakeCaches:
61
62    def get_cache(self, name, miss_fn):
63        return FakeCache(name, miss_fn)
64
65
66class FakeBuilder:
67
68    def __init__(self, master=None, buildername="Builder"):
69        if master:
70            self.master = master
71            self.botmaster = master.botmaster
72        self.name = buildername
73
74
75class FakeLogRotation:
76    rotateLength = 42
77    maxRotatedFiles = 42
78
79
80class FakeMaster(service.MasterService):
81
82    """
83    Create a fake Master instance: a Mock with some convenience
84    implementations:
85
86    - Non-caching implementation for C{self.caches}
87    """
88
89    def __init__(self, reactor,
90                 master_id=fakedb.FakeBuildRequestsComponent.MASTER_ID):
91        super().__init__()
92        self._master_id = master_id
93        self.reactor = reactor
94        self.objectids = {}
95        self.config = config.MasterConfig()
96        self.caches = FakeCaches()
97        self.pbmanager = pbmanager.FakePBManager()
98        self.initLock = defer.DeferredLock()
99        self.basedir = 'basedir'
100        self.botmaster = FakeBotMaster()
101        self.botmaster.setServiceParent(self)
102        self.name = 'fake:/master'
103        self.masterid = master_id
104        self.workers = bworkermanager.FakeWorkerManager()
105        self.workers.setServiceParent(self)
106        self.machine_manager = FakeMachineManager()
107        self.machine_manager.setServiceParent(self)
108        self.log_rotation = FakeLogRotation()
109        self.db = mock.Mock()
110        self.next_objectid = 0
111        self.config_version = 0
112
113        def getObjectId(sched_name, class_name):
114            k = (sched_name, class_name)
115            try:
116                rv = self.objectids[k]
117            except KeyError:
118                rv = self.objectids[k] = self.next_objectid
119                self.next_objectid += 1
120            return defer.succeed(rv)
121        self.db.state.getObjectId = getObjectId
122
123    def getObjectId(self):
124        return defer.succeed(self._master_id)
125
126    def subscribeToBuildRequests(self, callback):
127        pass
128
129# Leave this alias, in case we want to add more behavior later
130
131
132def make_master(testcase, wantMq=False, wantDb=False, wantData=False,
133                wantRealReactor=False, wantGraphql=False, url=None, **kwargs):
134    if wantRealReactor:
135        _reactor = reactor
136    else:
137        assert testcase is not None, "need testcase for fake reactor"
138        # The test case must inherit from TestReactorMixin and setup it.
139        _reactor = testcase.reactor
140
141    master = FakeMaster(_reactor, **kwargs)
142    if url:
143        master.buildbotURL = url
144    if wantData:
145        wantMq = wantDb = True
146    if wantMq:
147        assert testcase is not None, "need testcase for wantMq"
148        master.mq = fakemq.FakeMQConnector(testcase)
149        master.mq.setServiceParent(master)
150    if wantDb:
151        assert testcase is not None, "need testcase for wantDb"
152        master.db = fakedb.FakeDBConnector(testcase)
153        master.db.setServiceParent(master)
154    if wantData:
155        master.data = fakedata.FakeDataConnector(master, testcase)
156    if wantGraphql:
157        master.graphql = GraphQLConnector()
158        master.graphql.setServiceParent(master)
159        master.graphql.data = master.data.realConnector
160        master.data._scanModule(endpoint)
161        master.config.www = {'graphql': {"debug": True}}
162        try:
163            master.graphql.reconfigServiceWithBuildbotConfig(master.config)
164        except ImportError:
165            pass
166    return master
167