Add API to upload a patched build for a custom A/B testing
[WebKit.git] / Websites / perf.webkit.org / public / v3 / models / commit-set.js
1 'use strict';
2
3 class CommitSet extends DataModelObject {
4
5     constructor(id, object)
6     {
7         super(id);
8         this._repositories = [];
9         this._repositoryToCommitMap = new Map;
10         this._repositoryToPatchMap = new Map;
11         this._repositoryToRootMap = new Map;
12         this._latestCommitTime = null;
13         this._customRoots = [];
14         this._allRootFiles = [];
15
16         if (!object)
17             return;
18
19         this._updateFromObject(object);
20     }
21
22     updateSingleton(object)
23     {
24         this._repositoryToCommitMap.clear();
25         this._repositoryToPatchMap.clear();
26         this._repositoryToRootMap.clear();
27         this._repositories = [];
28         this._updateFromObject(object);
29     }
30
31     _updateFromObject(object)
32     {
33         const rootFiles = new Set;
34         for (const item of object.revisionItems) {
35             const commit = item.commit;
36             console.assert(commit instanceof CommitLog);
37             console.assert(!item.patch || item.patch instanceof UploadedFile);
38             console.assert(!item.rootFile || item.rootFile instanceof UploadedFile);
39             const repository = commit.repository();
40             this._repositoryToCommitMap.set(repository, commit);
41             this._repositoryToPatchMap.set(repository, item.patch);
42             this._repositoryToRootMap.set(repository, item.rootFile);
43             if (item.rootFile)
44                 rootFiles.add(item.rootFile);
45             this._repositories.push(commit.repository());
46         }
47         this._customRoots = object.customRoots;
48         this._allRootFiles = Array.from(rootFiles).concat(object.customRoots);
49     }
50
51     repositories() { return this._repositories; }
52     customRoots() { return this._customRoots; }
53     allRootFiles() { return this._allRootFiles; }
54     commitForRepository(repository) { return this._repositoryToCommitMap.get(repository); }
55
56     revisionForRepository(repository)
57     {
58         var commit = this._repositoryToCommitMap.get(repository);
59         return commit ? commit.revision() : null;
60     }
61
62     patchForRepository(repository) { return this._repositoryToPatchMap.get(repository); }
63     rootForRepository(repository) { return this._repositoryToRootMap.get(repository); }
64
65     // FIXME: This should return a Date object.
66     latestCommitTime()
67     {
68         if (this._latestCommitTime == null) {
69             var maxTime = 0;
70             for (const [repository, commit] of this._repositoryToCommitMap)
71                 maxTime = Math.max(maxTime, +commit.time());
72             this._latestCommitTime = maxTime;
73         }
74         return this._latestCommitTime;
75     }
76
77     equals(other)
78     {
79         if (this._repositories.length != other._repositories.length)
80             return false;
81         for (const [repository, commit] of this._repositoryToCommitMap) {
82             if (commit != other._repositoryToCommitMap.get(repository))
83                 return false;
84             if (this._repositoryToPatchMap.get(repository) != other._repositoryToPatchMap.get(repository))
85                 return false;
86             if (this._repositoryToRootMap.get(repository) != other._repositoryToRootMap.get(repository))
87                 return false;
88         }
89         return CommitSet.areCustomRootsEqual(this._customRoots, other._customRoots);
90     }
91
92     static areCustomRootsEqual(customRoots1, customRoots2)
93     {
94         if (customRoots1.length != customRoots2.length)
95             return false;
96         const set2 = new Set(customRoots2);
97         for (let file of customRoots1) {
98             if (!set2.has(file))
99                 return false;
100         }
101         return true;
102     }
103
104     static containsMultipleCommitsForRepository(commitSets, repository)
105     {
106         console.assert(repository instanceof Repository);
107         if (commitSets.length < 2)
108             return false;
109         const firstCommit = commitSets[0].commitForRepository(repository);
110         for (let set of commitSets) {
111             const anotherCommit = set.commitForRepository(repository);
112             if (!firstCommit != !anotherCommit || (firstCommit && firstCommit.revision() != anotherCommit.revision()))
113                 return true;
114         }
115         return false;
116     }
117 }
118
119 class MeasurementCommitSet extends CommitSet {
120
121     constructor(id, revisionList)
122     {
123         super(id, null);
124         for (var values of revisionList) {
125             // [<commit-id>, <repository-id>, <revision>, <time>]
126             var commitId = values[0];
127             var repositoryId = values[1];
128             var revision = values[2];
129             var time = values[3];
130             var repository = Repository.findById(repositoryId);
131             if (!repository)
132                 continue;
133
134             // FIXME: Add a flag to remember the fact this commit log is incomplete.
135             const commit = CommitLog.ensureSingleton(commitId, {repository: repository, revision: revision, time: time});
136             this._repositoryToCommitMap.set(repository, commit);
137             this._repositories.push(repository);
138         }
139     }
140
141     // Use CommitSet's static maps because MeasurementCommitSet and CommitSet are logically of the same type.
142     // FIXME: Idaelly, DataModel should take care of this but traversing prototype chain is expensive.
143     namedStaticMap(name) { return CommitSet.namedStaticMap(name); }
144     ensureNamedStaticMap(name) { return CommitSet.ensureNamedStaticMap(name); }
145     static namedStaticMap(name) { return CommitSet.namedStaticMap(name); }
146     static ensureNamedStaticMap(name) { return CommitSet.ensureNamedStaticMap(name); }
147
148     static ensureSingleton(measurementId, revisionList)
149     {
150         const commitSetId = measurementId + '-commitset';
151         return CommitSet.findById(commitSetId) || (new MeasurementCommitSet(commitSetId, revisionList));
152     }
153 }
154
155 class CustomCommitSet {
156
157     constructor()
158     {
159         this._revisionListByRepository = new Map;
160         this._customRoots = [];
161     }
162
163     setRevisionForRepository(repository, revision, patch = null)
164     {
165         console.assert(repository instanceof Repository);
166         console.assert(!patch || patch instanceof UploadedFile);
167         this._revisionListByRepository.set(repository, {revision, patch});
168     }
169
170     equals(other)
171     {
172         console.assert(other instanceof CustomCommitSet);
173         if (this._revisionListByRepository.size != other._revisionListByRepository.size)
174             return false;
175         for (let repository of this._revisionListByRepository.keys()) {
176             const thisRevision = this._revisionListByRepository.get(repository);
177             const otherRevision = other._revisionListByRepository.get(repository);
178             if (!thisRevision != !otherRevision)
179                 return false;
180             if (thisRevision && (thisRevision.revision != otherRevision.revision
181                 || thisRevision.patch != otherRevision.patch))
182                 return false;
183         }
184         return CommitSet.areCustomRootsEqual(this._customRoots, other._customRoots);
185     }
186
187     repositories() { return Array.from(this._revisionListByRepository.keys()); }
188     revisionForRepository(repository)
189     {
190         const entry = this._revisionListByRepository.get(repository);
191         if (!entry)
192             return null;
193         return entry.revision;
194     }
195     patchForRepository(repository)
196     {
197         const entry = this._revisionListByRepository.get(repository);
198         if (!entry)
199             return null;
200         return entry.patch;
201     }
202     customRoots() { return this._customRoots; }
203
204     addCustomRoot(uploadedFile)
205     {
206         console.assert(uploadedFile instanceof UploadedFile);
207         this._customRoots.push(uploadedFile);
208     }
209 }
210
211 if (typeof module != 'undefined') {
212     module.exports.CommitSet = CommitSet;
213     module.exports.MeasurementCommitSet = MeasurementCommitSet;
214     module.exports.CustomCommitSet = CustomCommitSet;
215 }