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