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
16from twisted.internet import defer
17
18from buildbot.test.fakedb.base import FakeDBComponent
19from buildbot.test.fakedb.row import Row
20
21
22class BuildData(Row):
23    table = 'build_data'
24
25    id_column = 'id'
26    foreignKeys = ('buildid',)
27    required_columns = ('buildid', 'name', 'value', 'length', 'source')
28    binary_columns = ('value',)
29
30    def __init__(self, id=None, buildid=None, name=None, value=None, source=None):
31        super().__init__(id=id, buildid=buildid, name=name, value=value, source=source,
32                         length=len(value))
33
34
35class FakeBuildDataComponent(FakeDBComponent):
36
37    def setUp(self):
38        self.build_data = {}
39
40    def insertTestData(self, rows):
41        for row in rows:
42            if isinstance(row, BuildData):
43                self.build_data[row.id] = row.values.copy()
44
45    def _get_build_data_row(self, buildid, name):
46        for row in self.build_data.values():
47            if row['buildid'] == buildid and row['name'] == name:
48                return row
49        return None
50
51    def setBuildData(self, buildid, name, value, source):
52        assert isinstance(value, bytes)
53        row = self._get_build_data_row(buildid, name)
54        if row is not None:
55            row['value'] = value
56            row['length'] = len(value)
57            row['source'] = source
58            return
59
60        id = Row.nextId()
61        self.build_data[id] = {
62            'id': id,
63            'buildid': buildid,
64            'name': name,
65            'value': value,
66            'length': len(value),
67            'source': source
68        }
69
70    # returns a Deferred
71    def getBuildData(self, buildid, name):
72        row = self._get_build_data_row(buildid, name)
73        if row is not None:
74            return defer.succeed(self._row2dict(row))
75        return defer.succeed(None)
76
77    # returns a Deferred
78    def getBuildDataNoValue(self, buildid, name):
79        row = self._get_build_data_row(buildid, name)
80        if row is not None:
81            return defer.succeed(self._row2dict_novalue(row))
82        return defer.succeed(None)
83
84    # returns a Deferred
85    def getAllBuildDataNoValues(self, buildid):
86        ret = []
87        for row in self.build_data.values():
88            if row['buildid'] != buildid:
89                continue
90            ret.append(self._row2dict_novalue(row))
91
92        return defer.succeed(ret)
93
94    # returns a Deferred
95    def deleteOldBuildData(self, older_than_timestamp):
96        buildids_to_keep = []
97        for build_dict in self.db.builds.builds.values():
98            if build_dict['complete_at'] is None or \
99                    build_dict['complete_at'] >= older_than_timestamp:
100                buildids_to_keep.append(build_dict['id'])
101
102        count_before = len(self.build_data)
103
104        build_dataids_to_remove = []
105        for build_datadict in self.build_data.values():
106            if build_datadict['buildid'] not in buildids_to_keep:
107                build_dataids_to_remove.append(build_datadict['id'])
108
109        for id in build_dataids_to_remove:
110            self.build_data.pop(id)
111
112        count_after = len(self.build_data)
113
114        return defer.succeed(count_before - count_after)
115
116    def _row2dict(self, row):
117        ret = row.copy()
118        del ret['id']
119        return ret
120
121    def _row2dict_novalue(self, row):
122        ret = row.copy()
123        del ret['id']
124        ret['value'] = None
125        return ret
126