Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: appengine/findit/util_scripts/crash_queries/delta_test/delta_test.py

Issue 2400283003: [Findit] Add skeleton code for delta test script. (Closed)
Patch Set: Rebase. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import json
6 import logging
7 import os
8 import pickle
9 import subprocess
10
11 from crash_queries import crash_iterator
12 from crash_queries.delta_test import delta_util
13
14 AZALEA_RESULTS_DIRECTORY = os.path.join(os.path.dirname(__file__),
15 'azalea_results')
16 DELTA_TEST_DIRECTORY = os.path.dirname(__file__)
17
18
19 class Delta(object):
20 """Stands for delta between two results.
21
22 Note, the 2 results should be the same kind and have the same structure.
23 """
24
25 def __init__(self, result1, result2, fields):
26 self._result1 = result1
27 self._result2 = result2
28 self._fields = fields
29 self._delta_dict = {}
30 self._delta_str_dict = {}
31
32 @property
33 def delta_dict(self):
34 """Dict representation of delta.
35
36 Returns:
37 A dict. For example, for Culprit result, the delta dict is like below:
38 {
39 'project': 'chromium',
40 'components': ['Blink>API'],
41 'cls': [],
42 'regression_range': ['52.0.1200.1', '52.0.1200.3']
43 }
44 """
45 if self._delta_dict:
46 return self._delta_dict
47
48 for field in self._fields:
49 value1 = getattr(self._result1, field)
50 value2 = getattr(self._result2, field)
51 if value1 != value2:
52 if hasattr(value1, 'ToDict') and callable(value1.ToDict):
53 value1 = value1.ToDict()
54 value2 = value2.ToDict()
55 self._delta_dict[field] = (value1, value2)
56
57 return self._delta_dict
58
59 @property
60 def delta_str_dict(self):
61 """Converts delta of each field to a string."""
62 if self._delta_str_dict:
63 return self._delta_str_dict
64
65 for key, (value1, value2) in self.delta_dict.iteritems():
66 self._delta_str_dict[key] = '%s: %s, %s' % (key, value1, value2)
67
68 return self._delta_str_dict
69
70 def ToDict(self):
71 return self.delta_dict
72
73 def __str__(self):
74 return '\n'.join(self.delta_str_dict.values())
75
76 def __bool__(self):
77 return bool(self.delta_dict)
78
79 def __nonzero__(self):
80 return self.__bool__()
81
82
83 def GetDeltasFromTwoSetsOfResults(set1, set2):
84 """Gets delta from two sets of results.
85
86 Set1 and set2 are dicts mapping id to result.
87 Results are a list of (message, matches, component_name, cr_label)
88 Returns a list of delta results (results1, results2).
89 """
90 deltas = {}
91 for result_id, result1 in set1.iteritems():
92 # Even when the command are exactly the same, it's possible that one set is
93 # loaded from local result file, another is just queried from database,
94 # sometimes some crash results would get deleted.
95 if result_id not in set2:
96 continue
97
98 result2 = set2[result_id]
99 delta = Delta(result1, result2, result1.fields)
100 if delta:
101 deltas[result_id] = delta
102
103 return deltas
104
105
106 def GetResults(crashes, client_id, git_hash, result_path, verbose=False):
107 """Returns an evaluator function to compute delta between 2 findit githashes.
108
109 Args:
110 crashes (list): A list of crash infos.
111 client_id (str): Possible values - fracas/cracas/clustefuzz.
112 git_hash (str): A git hash of findit repository.
113 result_path (str): file path for subprocess to write results on.
114 verbose (bool): If True, print all the findit results.
115
116 Return:
117 A dict mapping crash id to culprit for every crashes analyzed by
118 git_hash version.
119 """
120 if not crashes:
121 return {}
122
123 if verbose:
124 logging.info('\n\n***************************')
125 logging.info('Switching to git %s', git_hash)
126 logging.info('***************************\n\n')
127
128 with open(os.devnull, 'w') as null_handle:
129 subprocess.check_call(
130 'cd %s; git checkout %s' % (DELTA_TEST_DIRECTORY, git_hash),
131 stdout=null_handle,
132 stderr=null_handle,
133 shell=True)
134
135 if not os.path.exists(result_path):
136 args = ['python', 'run-predator.py', result_path, '--client', client_id]
137 if verbose:
138 args.append('--verbose')
139 p = subprocess.Popen(args, stdin=subprocess.PIPE)
140 # TODO(katesonia): Cache crashes for crash_iterator and let subprocess read
141 # corresponding cache file instead.
142 p.communicate(input=json.dumps(crashes))
143 else:
144 logging.info('\nLoading results from %s', result_path)
145
146 if not os.path.exists(result_path):
147 logging.error('Failed to get results.')
148 return {}
149
150 with open(result_path) as f:
151 return pickle.load(f)
152
153 return {}
154
155
156 def DeltaEvaluator(git_hash1, git_hash2,
157 client_id, app_id,
158 start_date, end_date, batch_size,
159 property_values=None, verbose=False):
160 """Evaluates delta between git_hash1 and git_hash2 on a set of Testcases.
161
162 Args:
163 git_hash1 (str): A git hash of findit repository.
164 git_hash2 (str): A git hash of findit repository.
165 start_date (str): Run delta test on testcases after (including)
166 the start_date, format should be '%Y-%m-%d'.
167 end_date (str): Run delta test on testcases before (not including)
168 the end_date, format should be '%Y-%m-%d'.
169 client_id (CrashClient): Possible values are 'fracas', 'cracas',
170 'cluterfuzz'.
171 app_id (str): Appengine app id to query.
172 batch_size (int): Size of a batch that can be queried at one time.
173 property_values (dict): Property values to query.
174 batch_size (int): The size of crashes that can be queried at one time.
175 verbose (bool): If True, print all the findit results.
176 Return:
177 (deltas, crash_count).
178 deltas (dict): Mappings id to delta for each culprit value.
179 crash_count (int): Total count of all the crashes.
180 """
181 head_branch_name = subprocess.check_output(
182 ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).replace('\n', '')
183 try:
184 deltas = {}
185 crash_count = 0
186 for index, crashes in enumerate(
187 crash_iterator.IterateCrashes(client_id, app_id,
188 property_values=property_values,
189 start_date=start_date,
190 end_date=end_date,
191 batch_size=batch_size,
192 batch_run=True)):
193
194 results = []
195 for git_hash in [git_hash1, git_hash2]:
196 result_path = os.path.join(
197 AZALEA_RESULTS_DIRECTORY, delta_util.GenerateFileName(
198 client_id, property_values, start_date, end_date,
199 batch_size, index, git_hash))
200 results.append(GetResults(crashes, client_id, git_hash, result_path,
201 verbose=verbose))
202
203 crash_count += len(crashes)
204 deltas.update(GetDeltasFromTwoSetsOfResults(*results))
205
206 return deltas, crash_count
207 finally:
208 with open(os.devnull, 'w') as null_handle:
209 subprocess.check_call(['git', 'checkout', head_branch_name],
210 stdout=null_handle,
211 stderr=null_handle)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698