aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/compare_baselines.py
blob: 7268eb7a54639a28c19091af6d842e02bba0e845 (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
'''
Compares the gm results within the local checkout against those already
committed to the Skia repository.

Launch with --help to see more information.


Copyright 2012 Google Inc.

Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
'''

# common Python modules
import fnmatch
import optparse
import os
import shutil
import tempfile

# modules declared within this same directory
import svn

USAGE_STRING = 'Usage: %s [options]'
HOWTO_STRING = '''
To update the checked-in baselines across all platforms, follow these steps:

cd .../trunk
svn update
svn stat   # and make sure there are no files awaiting svn commit
make tools BUILDTYPE=Release
python tools/download_baselines.py
python tools/compare_baselines.py
# view compare_baselines output in a browser and make sure it's reasonable
# upload CL for review
# validate that the diffs look right in the review tool
# commit CL

Note that the above instructions will only *update* already-checked-in
baseline images; if you want to check in new baseline images (ones that the
bots have been generating but we don't have a golden master for yet), you need
to use download_baselines.py's --add-new-files option.
'''
HELP_STRING = '''

Compares the gm results within the local checkout against those already
committed to the Skia repository. Relies on skdiff to do the low-level
comparison.

''' + HOWTO_STRING

TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir)

OPTION_GM_BASEDIR = '--gm-basedir'
DEFAULT_GM_BASEDIR = os.path.join(TRUNK_PATH, os.pardir, 'gm-expected')
OPTION_PATH_TO_SKDIFF = '--path-to-skdiff'
# default PATH_TO_SKDIFF is determined at runtime
OPTION_SVN_GM_URL = '--svn-gm-url'
DEFAULT_SVN_GM_URL = 'http://skia.googlecode.com/svn/gm-expected'

def CopyAllFilesAddingPrefix(source_dir, dest_dir, prefix):
    """Copy all files from source_dir into dest_dir, adding prefix to the name
    of each one as we copy it.
    prefixes.

    @param source_dir
    @param dest_dir where to save the copied files
    @param prefix prefix to add to each filename when we make the copy
    """
    all_filenames = os.listdir(source_dir)
    for filename in all_filenames:
        source_path = os.path.join(source_dir, filename)
        if os.path.isdir(source_path):
            print 'skipping %s because it is a directory, not a file' % filename
            continue
        dest_path = os.path.join(dest_dir, '%s%s' % (prefix, filename))
        shutil.copyfile(source_path, dest_path)

def Flatten(source_dir, dest_dir, subdirectory_pattern):
    """Copy all files from matching subdirectories under source_dir into
    dest_dir, flattened into a single directory using subdirectory names as
    prefixes.

    @param source_dir
    @param dest_dir where to save the copied files
    @param subdirectory_pattern only copy files from subdirectories that match
           this Unix-style filename pattern (e.g., 'base-*')
    """
    all_filenames = os.listdir(source_dir)
    matching_filenames = fnmatch.filter(all_filenames, subdirectory_pattern)
    for filename in matching_filenames:
        source_path = os.path.join(source_dir, filename)
        if not os.path.isdir(source_path):
            print 'skipping %s because it is a file, not a directory' % filename
            continue
        print 'flattening directory %s' % source_path
        CopyAllFilesAddingPrefix(source_dir=source_path, dest_dir=dest_dir,
                                 prefix='%s_' % filename)

def RunCommand(command):
    """Run a command, raising an exception if it fails.

    @param command the command as a single string
    """
    print 'running command [%s]...' % command
    retval = os.system(command)
    #if retval is not 0:
    #    raise Exception('command [%s] failed' % command)

def FindPathToSkDiff(user_set_path=None):
    """Return path to an existing skdiff binary, or raise an exception if we
    cannot find one.

    @param user_set_path if None, the user did not specify a path, so look in
           some likely places; otherwise, only check at this path
    """
    if user_set_path is not None:
        if os.path.isfile(user_set_path):
            return user_set_path
        raise Exception('unable to find skdiff at user-set path %s' %
                        user_set_path)
    trunk_path = os.path.join(os.path.dirname(__file__), os.pardir)
    possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'),
                      os.path.join(trunk_path, 'out', 'Debug', 'skdiff')]
    for try_path in possible_paths:
        if os.path.isfile(try_path):
            return try_path
    raise Exception('cannot find skdiff in paths %s; maybe you need to '
                    'specify the %s option or build skdiff?' % (
                        possible_paths, OPTION_PATH_TO_SKDIFF))

def CompareBaselines(gm_basedir, path_to_skdiff, svn_gm_url):
    """Compare the gm results within gm_basedir against those already
    committed to the Skia repository.

    @param gm_basedir
    @param path_to_skdiff
    @param svn_gm_url base URL of Subversion repository where we store the
           expected GM results
    """
    # Validate parameters, filling in default values if necessary and possible.
    if not os.path.isdir(gm_basedir):
        raise Exception('cannot find gm_basedir at %s; maybe you need to '
                        'specify the %s option?' % (
                            gm_basedir, OPTION_GM_BASEDIR))
    path_to_skdiff = FindPathToSkDiff(path_to_skdiff)

    tempdir_base = tempfile.mkdtemp()

    # Download all checked-in baseline images to a temp directory
    checkedin_dir = os.path.join(tempdir_base, 'checkedin')
    os.mkdir(checkedin_dir)
    svn.Svn(checkedin_dir).Checkout(svn_gm_url, '.')

    # Flatten those checked-in baseline images into checkedin_flattened_dir
    checkedin_flattened_dir = os.path.join(tempdir_base, 'checkedin_flattened')
    os.mkdir(checkedin_flattened_dir)
    Flatten(source_dir=checkedin_dir, dest_dir=checkedin_flattened_dir,
            subdirectory_pattern='base-*')

    # Flatten the local baseline images into local_flattened_dir
    local_flattened_dir = os.path.join(tempdir_base, 'local_flattened')
    os.mkdir(local_flattened_dir)
    Flatten(source_dir=gm_basedir, dest_dir=local_flattened_dir,
            subdirectory_pattern='base-*')

    # Run skdiff to compare checkedin_flattened_dir against local_flattened_dir
    diff_dir = os.path.join(tempdir_base, 'diffs')
    os.mkdir(diff_dir)
    RunCommand('%s %s %s %s' % (path_to_skdiff, checkedin_flattened_dir,
                                local_flattened_dir, diff_dir))
    print '\nskdiff results are ready in file://%s/index.html' % diff_dir
    # TODO(epoger): delete tempdir_base tree to clean up after ourselves (but
    # not before the user gets a chance to examine the results), and/or
    # allow user to specify a different directory to write into?

def RaiseUsageException():
    raise Exception('%s\nRun with --help for more detail.' % (
        USAGE_STRING % __file__))

def Main(options, args):
    """Allow other scripts to call this script with fake command-line args.
    """
    num_args = len(args)
    if num_args != 0:
        RaiseUsageException()
    CompareBaselines(gm_basedir=options.gm_basedir,
                     path_to_skdiff=options.path_to_skdiff,
                     svn_gm_url=options.svn_gm_url)

if __name__ == '__main__':
    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
    parser.add_option(OPTION_GM_BASEDIR,
                      action='store', type='string', default=DEFAULT_GM_BASEDIR,
                      help='path to root of locally stored baseline images '
                      'to compare against those checked into the svn repo; '
                      'defaults to "%s"' % DEFAULT_GM_BASEDIR)
    parser.add_option(OPTION_PATH_TO_SKDIFF,
                      action='store', type='string', default=None,
                      help='path to already-built skdiff tool; if not set, '
                      'will search for it in typical directories near this '
                      'script')
    parser.add_option(OPTION_SVN_GM_URL,
                      action='store', type='string', default=DEFAULT_SVN_GM_URL,
                      help='URL of SVN repository within which we store the '
                      'expected GM baseline images; defaults to "%s"' %
                      DEFAULT_SVN_GM_URL)
    (options, args) = parser.parse_args()
    Main(options, args)