1cdf0c1d5Smjnelson#! /usr/bin/python 2cdf0c1d5Smjnelson# 3cdf0c1d5Smjnelson# CDDL HEADER START 4cdf0c1d5Smjnelson# 5cdf0c1d5Smjnelson# The contents of this file are subject to the terms of the 6cdf0c1d5Smjnelson# Common Development and Distribution License (the "License"). 7cdf0c1d5Smjnelson# You may not use this file except in compliance with the License. 8cdf0c1d5Smjnelson# 9cdf0c1d5Smjnelson# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10cdf0c1d5Smjnelson# or http://www.opensolaris.org/os/licensing. 11cdf0c1d5Smjnelson# See the License for the specific language governing permissions 12cdf0c1d5Smjnelson# and limitations under the License. 13cdf0c1d5Smjnelson# 14cdf0c1d5Smjnelson# When distributing Covered Code, include this CDDL HEADER in each 15cdf0c1d5Smjnelson# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16cdf0c1d5Smjnelson# If applicable, add the following below this CDDL HEADER, with the 17cdf0c1d5Smjnelson# fields enclosed by brackets "[]" replaced with your own identifying 18cdf0c1d5Smjnelson# information: Portions Copyright [yyyy] [name of copyright owner] 19cdf0c1d5Smjnelson# 20cdf0c1d5Smjnelson# CDDL HEADER END 21cdf0c1d5Smjnelson# 22cdf0c1d5Smjnelson 23cdf0c1d5Smjnelson# 24*41a5f560SMark J. Nelson# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25cdf0c1d5Smjnelson# Use is subject to license terms. 26cdf0c1d5Smjnelson# 27cdf0c1d5Smjnelson 28cdf0c1d5Smjnelson# 29cdf0c1d5Smjnelson# Check delta comments: 30cdf0c1d5Smjnelson# - Have the correct form. 31cdf0c1d5Smjnelson# - Have a synopsis matching that of the CR or ARC case. 32cdf0c1d5Smjnelson# - Appear only once. 33cdf0c1d5Smjnelson# 34cdf0c1d5Smjnelson 35cdf0c1d5Smjnelsonimport re, sys 36cdf0c1d5Smjnelsonfrom onbld.Checks.DbLookups import BugDB, ARC 37cdf0c1d5Smjnelson 38cdf0c1d5Smjnelsonarcre = re.compile(r'^([A-Z][A-Z]*ARC[/ \t][12]\d{3}/\d{3}) (.*)$') 39cdf0c1d5Smjnelsonbugre = re.compile(r'^(\d{7}) (.*)$') 40ef62fef3SRichard Lowe 41cdf0c1d5Smjnelsondef isARC(comment): 42cdf0c1d5Smjnelson return arcre.match(comment) 43cdf0c1d5Smjnelson 44cdf0c1d5Smjnelsondef isBug(comment): 45cdf0c1d5Smjnelson return bugre.match(comment) 46cdf0c1d5Smjnelson 47cdf0c1d5Smjnelson# 48cdf0c1d5Smjnelson# Translate any acceptable case number format into "<ARC> <YEAR>/<NUM>" 49cdf0c1d5Smjnelson# format. 50cdf0c1d5Smjnelson# 51cdf0c1d5Smjnelsondef normalize_arc(caseid): 52cdf0c1d5Smjnelson return re.sub(r'^([A-Z][A-Z]*ARC)[/ \t]', '\\1 ', caseid) 53cdf0c1d5Smjnelson 54*41a5f560SMark J. Nelsondef comchk(comments, check_db=True, output=sys.stderr, arcPath=None): 55ef62fef3SRichard Lowe '''Validate checkin comments against ON standards. 56ef62fef3SRichard Lowe 57ef62fef3SRichard Lowe Comments must be a list of one-line comments, with no trailing 58ef62fef3SRichard Lowe newline. 59ef62fef3SRichard Lowe 60ef62fef3SRichard Lowe If check_db is True (the default), validate CR and ARC 61ef62fef3SRichard Lowe synopses against the databases. 62ef62fef3SRichard Lowe 63ef62fef3SRichard Lowe Error messages intended for the user are written to output, 64ef62fef3SRichard Lowe which defaults to stderr 65ef62fef3SRichard Lowe ''' 66cdf0c1d5Smjnelson bugnospcre = re.compile(r'^(\d{7})([^ ].*)') 67aa46c263SJohn Beck ignorere = re.compile(r'^(Portions contributed by |Contributed by |back[ -]?out )') 68cdf0c1d5Smjnelson 69cdf0c1d5Smjnelson errors = { 'bugnospc': [], 70cdf0c1d5Smjnelson 'mutant': [], 71cdf0c1d5Smjnelson 'dup': [], 72cdf0c1d5Smjnelson 'nomatch': [], 73cdf0c1d5Smjnelson 'nonexistent': [] } 74cdf0c1d5Smjnelson bugs = {} 75cdf0c1d5Smjnelson arcs = {} 76ef62fef3SRichard Lowe ret = 0 77ef62fef3SRichard Lowe blanks = False 78cdf0c1d5Smjnelson 79cdf0c1d5Smjnelson for com in comments: 80ef62fef3SRichard Lowe # Our input must be newline-free, comments are line-wise. 81ef62fef3SRichard Lowe if com.find('\n') != -1: 82ef62fef3SRichard Lowe raise ValueError("newline in comment '%s'" % com) 83ef62fef3SRichard Lowe 84cdf0c1d5Smjnelson # Ignore valid comments we can't check 85cdf0c1d5Smjnelson if ignorere.search(com): 86cdf0c1d5Smjnelson continue 87cdf0c1d5Smjnelson 88cdf0c1d5Smjnelson if not com or com.isspace(): 89ef62fef3SRichard Lowe blanks = True 90cdf0c1d5Smjnelson continue 91cdf0c1d5Smjnelson 92cdf0c1d5Smjnelson match = bugre.search(com) 93cdf0c1d5Smjnelson if match: 94cdf0c1d5Smjnelson if match.group(1) not in bugs: 95cdf0c1d5Smjnelson bugs[match.group(1)] = [] 96cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 97cdf0c1d5Smjnelson continue 98cdf0c1d5Smjnelson 99cdf0c1d5Smjnelson # 100cdf0c1d5Smjnelson # Bugs missing a space after the ID are still bugs 101cdf0c1d5Smjnelson # for the purposes of the duplicate ID and synopsis 102cdf0c1d5Smjnelson # checks. 103cdf0c1d5Smjnelson # 104cdf0c1d5Smjnelson match = bugnospcre.search(com) 105cdf0c1d5Smjnelson if match: 106cdf0c1d5Smjnelson if match.group(1) not in bugs: 107cdf0c1d5Smjnelson bugs[match.group(1)] = [] 108cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 109cdf0c1d5Smjnelson errors['bugnospc'].append(com) 110cdf0c1d5Smjnelson continue 111cdf0c1d5Smjnelson 112cdf0c1d5Smjnelson # ARC case 113cdf0c1d5Smjnelson match = arcre.search(com) 114cdf0c1d5Smjnelson if match: 115c08a253cSJohn Sonnenschein arc, case = re.split('[/ \t]', match.group(1), 1) 116c08a253cSJohn Sonnenschein arcs.setdefault((arc, case), []).append(match.group(2)) 117cdf0c1d5Smjnelson continue 118cdf0c1d5Smjnelson 119cdf0c1d5Smjnelson # Anything else is bogus 120cdf0c1d5Smjnelson errors['mutant'].append(com) 121cdf0c1d5Smjnelson 122cdf0c1d5Smjnelson if len(bugs) > 0 and check_db: 123cdf0c1d5Smjnelson bugdb = BugDB() 124cdf0c1d5Smjnelson results = bugdb.lookup(bugs.keys()) 125cdf0c1d5Smjnelson 126cdf0c1d5Smjnelson for crid, insts in bugs.iteritems(): 127cdf0c1d5Smjnelson if len(insts) > 1: 128cdf0c1d5Smjnelson errors['dup'].append(crid) 129cdf0c1d5Smjnelson 130cdf0c1d5Smjnelson if not check_db: 131cdf0c1d5Smjnelson continue 132cdf0c1d5Smjnelson 133cdf0c1d5Smjnelson if crid not in results: 134cdf0c1d5Smjnelson errors['nonexistent'].append(crid) 135cdf0c1d5Smjnelson continue 136cdf0c1d5Smjnelson 137ef62fef3SRichard Lowe # 138ef62fef3SRichard Lowe # For each synopsis, compare the real synopsis with 139ef62fef3SRichard Lowe # that in the comments, allowing for possible '(fix 140ef62fef3SRichard Lowe # stuff)'-like trailing text 141ef62fef3SRichard Lowe # 142cdf0c1d5Smjnelson for entered in insts: 143cdf0c1d5Smjnelson synopsis = results[crid]["synopsis"] 144ef62fef3SRichard Lowe if not re.search(r'^' + re.escape(synopsis) + 145cdf0c1d5Smjnelson r'( \([^)]+\))?$', entered): 146cdf0c1d5Smjnelson errors['nomatch'].append([crid, synopsis, 147cdf0c1d5Smjnelson entered]) 148cdf0c1d5Smjnelson 149c08a253cSJohn Sonnenschein if check_db: 150*41a5f560SMark J. Nelson valid = ARC(arcs.keys(), arcPath) 151c08a253cSJohn Sonnenschein 152cdf0c1d5Smjnelson for case, insts in arcs.iteritems(): 153cdf0c1d5Smjnelson if len(insts) > 1: 154c08a253cSJohn Sonnenschein errors['dup'].append(' '.join(case)) 155cdf0c1d5Smjnelson 156cdf0c1d5Smjnelson if not check_db: 157cdf0c1d5Smjnelson continue 158cdf0c1d5Smjnelson 159c08a253cSJohn Sonnenschein if not case in valid: 160c08a253cSJohn Sonnenschein errors['nonexistent'].append(' '.join(case)) 161cdf0c1d5Smjnelson continue 162*41a5f560SMark J. Nelson 163cdf0c1d5Smjnelson # 164cdf0c1d5Smjnelson # We first try a direct match between the actual case name 165cdf0c1d5Smjnelson # and the entered comment. If that fails we remove a possible 166cdf0c1d5Smjnelson # trailing (fix nit)-type comment, and re-try. 167cdf0c1d5Smjnelson # 168cdf0c1d5Smjnelson for entered in insts: 169*41a5f560SMark J. Nelson if entered == valid[case]: 170c08a253cSJohn Sonnenschein break 171cdf0c1d5Smjnelson else: 172ef62fef3SRichard Lowe # Try again with trailing (fix ...) removed. 173cdf0c1d5Smjnelson dbcom = re.sub(r' \([^)]+\)$', '', entered) 174*41a5f560SMark J. Nelson if dbcom != valid[case]: 175c08a253cSJohn Sonnenschein errors['nomatch'].append( 176c08a253cSJohn Sonnenschein [' '.join(case), valid[case], 177cdf0c1d5Smjnelson entered]) 178cdf0c1d5Smjnelson 179cdf0c1d5Smjnelson if blanks: 180cdf0c1d5Smjnelson output.write("WARNING: Blank line(s) in comments\n") 181cdf0c1d5Smjnelson ret = 1 182cdf0c1d5Smjnelson 183cdf0c1d5Smjnelson if errors['dup']: 184cdf0c1d5Smjnelson ret = 1 185cdf0c1d5Smjnelson output.write("These IDs appear more than once in your " 186cdf0c1d5Smjnelson "comments:\n") 187cdf0c1d5Smjnelson for err in errors['dup']: 188cdf0c1d5Smjnelson output.write(" %s\n" % err) 189cdf0c1d5Smjnelson 190cdf0c1d5Smjnelson if errors['bugnospc']: 191cdf0c1d5Smjnelson ret = 1 192cdf0c1d5Smjnelson output.write("These bugs are missing a single space following " 193cdf0c1d5Smjnelson "the ID:\n") 194cdf0c1d5Smjnelson for com in errors['bugnospc']: 195cdf0c1d5Smjnelson output.write(" %s\n" % com) 196cdf0c1d5Smjnelson 197cdf0c1d5Smjnelson if errors['mutant']: 198cdf0c1d5Smjnelson ret = 1 199cdf0c1d5Smjnelson output.write("These comments are neither bug nor ARC case:\n") 200cdf0c1d5Smjnelson for com in errors['mutant']: 201cdf0c1d5Smjnelson output.write(" %s\n" % com) 202cdf0c1d5Smjnelson 203cdf0c1d5Smjnelson if errors['nonexistent']: 204cdf0c1d5Smjnelson ret = 1 205cdf0c1d5Smjnelson output.write("These bugs/ARC cases were not found in the " 206cdf0c1d5Smjnelson "databases:\n") 207cdf0c1d5Smjnelson for id in errors['nonexistent']: 208cdf0c1d5Smjnelson output.write(" %s\n" % id) 209cdf0c1d5Smjnelson 210cdf0c1d5Smjnelson if errors['nomatch']: 211cdf0c1d5Smjnelson ret = 1 212cdf0c1d5Smjnelson output.write("These bugs/ARC case synopsis/names don't match " 213cdf0c1d5Smjnelson "the database entries:\n") 214cdf0c1d5Smjnelson for err in errors['nomatch']: 215cdf0c1d5Smjnelson output.write("Synopsis/name of %s is wrong:\n" % err[0]) 216cdf0c1d5Smjnelson output.write(" should be: '%s'\n" % err[1]) 217cdf0c1d5Smjnelson output.write(" is: '%s'\n" % err[2]) 218cdf0c1d5Smjnelson 219cdf0c1d5Smjnelson return ret 220