summaryrefslogtreecommitdiff
path: root/src/main/java/com/android/vts/entity/CoverageEntity.java
blob: 50d2f4e2202d89acb574bc8845a6f7531ed56f06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
 * Copyright (c) 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.android.vts.entity;

import static com.googlecode.objectify.ObjectifyService.ofy;

import com.android.vts.proto.VtsReportMessage.CoverageReportMessage;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.googlecode.objectify.annotation.Cache;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@com.googlecode.objectify.annotation.Entity(name = "Coverage")
@Cache
@Data
@NoArgsConstructor
/** Object describing coverage data gathered for a file. */
public class CoverageEntity implements DashboardEntity {

    protected static final Logger logger = Logger.getLogger(CoverageEntity.class.getName());

    public static final String KIND = "Coverage";

    public static String GERRIT_URI;

    // Property keys
    public static final String GROUP = "group";
    public static final String COVERED_LINE_COUNT = "coveredCount";
    public static final String TOTAL_LINE_COUNT = "totalCount";
    public static final String FILE_PATH = "filePath";
    public static final String PROJECT_NAME = "projectName";
    public static final String PROJECT_VERSION = "projectVersion";
    public static final String LINE_COVERAGE = "lineCoverage";

    @Ignore @Getter @Setter private Key parentKey;

    @Id @Getter @Setter private Long id;

    @Parent @Getter @Setter private com.googlecode.objectify.Key<?> testParent;

    @Index @Getter @Setter private String group;

    @Getter @Setter private long coveredCount;

    @Getter @Setter private long totalCount;

    @Index @Getter @Setter private String filePath;

    @Getter @Setter private String projectName;

    @Getter @Setter private String projectVersion;

    @Getter @Setter private List<Long> lineCoverage;

    /** CoverageEntity isIgnored field */
    @Index @Getter @Setter Boolean isIgnored;

    /**
     * Create a CoverageEntity object for a file.
     *
     * @param parentKey The key to the parent TestRunEntity object in the database.
     * @param group The group within the test run describing the coverage.
     * @param coveredLineCount The total number of covered lines in the file.
     * @param totalLineCount The total number of uncovered executable lines in the file.
     * @param filePath The path to the file.
     * @param projectName The name of the git project.
     * @param projectVersion The commit hash of the project at the time the test was executed.
     * @param lineCoverage List of coverage counts per executable line in the file.
     */
    public CoverageEntity(
            Key parentKey,
            String group,
            long coveredLineCount,
            long totalLineCount,
            String filePath,
            String projectName,
            String projectVersion,
            List<Long> lineCoverage) {
        this.parentKey = parentKey;
        this.group = group;
        this.coveredCount = coveredLineCount;
        this.totalCount = totalLineCount;
        this.filePath = filePath;
        this.projectName = projectName;
        this.projectVersion = projectVersion;
        this.lineCoverage = lineCoverage;
    }

    /**
     * Create a CoverageEntity object for a file.
     *
     * @param testParent The objectify key to the parent TestRunEntity object in the database.
     * @param group The group within the test run describing the coverage.
     * @param coveredLineCount The total number of covered lines in the file.
     * @param totalLineCount The total number of uncovered executable lines in the file.
     * @param filePath The path to the file.
     * @param projectName The name of the git project.
     * @param projectVersion The commit hash of the project at the time the test was executed.
     * @param lineCoverage List of coverage counts per executable line in the file.
     */
    public CoverageEntity(
            com.googlecode.objectify.Key testParent,
            String group,
            long coveredLineCount,
            long totalLineCount,
            String filePath,
            String projectName,
            String projectVersion,
            List<Long> lineCoverage) {
        this.testParent = testParent;
        this.group = group;
        this.coveredCount = coveredLineCount;
        this.totalCount = totalLineCount;
        this.filePath = filePath;
        this.projectName = projectName;
        this.projectVersion = projectVersion;
        this.lineCoverage = lineCoverage;
    }

    /** find coverage entity by ID */
    public static CoverageEntity findById(String testName, String testRunId, String id) {
        com.googlecode.objectify.Key testKey =
                com.googlecode.objectify.Key.create(TestEntity.class, testName);
        com.googlecode.objectify.Key testRunKey =
                com.googlecode.objectify.Key.create(
                        testKey, TestRunEntity.class, Long.parseLong(testRunId));
        return ofy().load()
                .type(CoverageEntity.class)
                .parent(testRunKey)
                .id(Long.parseLong(id))
                .now();
    }

    public static void setPropertyValues(Properties newSystemConfigProp) {
        GERRIT_URI = newSystemConfigProp.getProperty("gerrit.uri");
    }

    /** Saving function for the instance of this class */
    @Override
    public com.googlecode.objectify.Key<CoverageEntity> save() {
        return ofy().save().entity(this).now();
    }

    /** Get percentage from calculating coveredCount and totalCount values */
    public Double getPercentage() {
        return Math.round(coveredCount * 10000d / totalCount) / 100d;
    }

    /** Get Gerrit Url function from the attributes of this class */
    public String getGerritUrl() throws UnsupportedEncodingException {
        String gerritPath =
                GERRIT_URI
                        + "/projects/"
                        + URLEncoder.encode(projectName, "UTF-8")
                        + "/commits/"
                        + URLEncoder.encode(projectVersion, "UTF-8")
                        + "/files/"
                        + URLEncoder.encode(filePath, "UTF-8")
                        + "/content";
        return gerritPath;
    }

    /* Comparator for sorting the list by isIgnored field */
    public static Comparator<CoverageEntity> isIgnoredComparator =
            new Comparator<CoverageEntity>() {

                public int compare(CoverageEntity coverageEntity1, CoverageEntity coverageEntity2) {
                    Boolean isIgnored1 =
                            Objects.isNull(coverageEntity1.getIsIgnored())
                                    ? false
                                    : coverageEntity1.getIsIgnored();
                    Boolean isIgnored2 =
                            Objects.isNull(coverageEntity2.getIsIgnored())
                                    ? false
                                    : coverageEntity2.getIsIgnored();

                    // ascending order
                    return isIgnored1.compareTo(isIgnored2);
                }
            };

    public Entity toEntity() {
        Entity coverageEntity = new Entity(KIND, parentKey);
        coverageEntity.setProperty(GROUP, group);
        coverageEntity.setUnindexedProperty(COVERED_LINE_COUNT, coveredCount);
        coverageEntity.setUnindexedProperty(TOTAL_LINE_COUNT, totalCount);
        coverageEntity.setProperty(FILE_PATH, filePath);
        coverageEntity.setUnindexedProperty(PROJECT_NAME, projectName);
        coverageEntity.setUnindexedProperty(PROJECT_VERSION, projectVersion);
        if (lineCoverage != null && lineCoverage.size() > 0) {
            coverageEntity.setUnindexedProperty(LINE_COVERAGE, lineCoverage);
        }
        return coverageEntity;
    }

    /**
     * Convert an Entity object to a CoverageEntity.
     *
     * @param e The entity to process.
     * @return CoverageEntity object with the properties from e, or null if incompatible.
     */
    @SuppressWarnings("unchecked")
    public static CoverageEntity fromEntity(Entity e) {
        if (!e.getKind().equals(KIND)
                || !e.hasProperty(GROUP)
                || !e.hasProperty(COVERED_LINE_COUNT)
                || !e.hasProperty(TOTAL_LINE_COUNT)
                || !e.hasProperty(FILE_PATH)
                || !e.hasProperty(PROJECT_NAME)
                || !e.hasProperty(PROJECT_VERSION)) {
            logger.log(Level.WARNING, "Missing coverage attributes in entity: " + e.toString());
            return null;
        }
        try {
            String group = (String) e.getProperty(GROUP);
            long coveredLineCount = (long) e.getProperty(COVERED_LINE_COUNT);
            long totalLineCount = (long) e.getProperty(TOTAL_LINE_COUNT);
            String filePath = (String) e.getProperty(FILE_PATH);
            String projectName = (String) e.getProperty(PROJECT_NAME);
            String projectVersion = (String) e.getProperty(PROJECT_VERSION);
            List<Long> lineCoverage;
            if (e.hasProperty(LINE_COVERAGE)) {
                lineCoverage = (List<Long>) e.getProperty(LINE_COVERAGE);
            } else {
                lineCoverage = new ArrayList<>();
            }
            return new CoverageEntity(
                    e.getKey().getParent(),
                    group,
                    coveredLineCount,
                    totalLineCount,
                    filePath,
                    projectName,
                    projectVersion,
                    lineCoverage);
        } catch (ClassCastException exception) {
            // Invalid contents or null values
            logger.log(Level.WARNING, "Error parsing coverage entity.", exception);
        }
        return null;
    }

    /**
     * Convert a coverage report to a CoverageEntity.
     *
     * @param parentKey The ancestor key for the coverage entity.
     * @param group The group to display the coverage report with.
     * @param coverage The coverage report containing coverage data.
     * @return The CoverageEntity for the coverage report message, or null if not compatible.
     */
    public static CoverageEntity fromCoverageReport(
            com.googlecode.objectify.Key parentKey, String group, CoverageReportMessage coverage) {
        if (!coverage.hasFilePath()
                || !coverage.hasProjectName()
                || !coverage.hasRevision()
                || !coverage.hasTotalLineCount()
                || !coverage.hasCoveredLineCount()) {
            return null; // invalid coverage report;
        }
        long coveredLineCount = coverage.getCoveredLineCount();
        long totalLineCount = coverage.getTotalLineCount();
        String filePath = coverage.getFilePath().toStringUtf8();
        String projectName = coverage.getProjectName().toStringUtf8();
        String projectVersion = coverage.getRevision().toStringUtf8();
        List<Long> lineCoverage = null;
        if (coverage.getLineCoverageVectorCount() > 0) {
            lineCoverage = new ArrayList<>();
            for (long count : coverage.getLineCoverageVectorList()) {
                lineCoverage.add(count);
            }
        }
        return new CoverageEntity(
                parentKey,
                group,
                coveredLineCount,
                totalLineCount,
                filePath,
                projectName,
                projectVersion,
                lineCoverage);
    }
}