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# 2441a5f560SMark J. Nelson# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25cdf0c1d5Smjnelson# Use is subject to license terms. 26cdf0c1d5Smjnelson# 27cdf0c1d5Smjnelson 28*2f54b716SRichard Lowe# Copyright 2007, 2010 Richard Lowe 29*2f54b716SRichard Lowe 30cdf0c1d5Smjnelson# 31cdf0c1d5Smjnelson# Check delta comments: 32cdf0c1d5Smjnelson# - Have the correct form. 33*2f54b716SRichard Lowe# - Have a synopsis matching that of the bug 34cdf0c1d5Smjnelson# - Appear only once. 35cdf0c1d5Smjnelson# 36cdf0c1d5Smjnelson 37cdf0c1d5Smjnelsonimport re, sys 38*2f54b716SRichard Lowefrom onbld.Checks.DbLookups import BugDB 39cdf0c1d5Smjnelson 40ef62fef3SRichard Lowe 41*2f54b716SRichard Lowebugre = re.compile(r'^(\d{2,7}) (.*)$') 42*2f54b716SRichard Lowe 43cdf0c1d5Smjnelson 44cdf0c1d5Smjnelsondef isBug(comment): 45cdf0c1d5Smjnelson return bugre.match(comment) 46cdf0c1d5Smjnelson 47cdf0c1d5Smjnelson 48*2f54b716SRichard Lowedef comchk(comments, check_db=True, output=sys.stderr): 49ef62fef3SRichard Lowe '''Validate checkin comments against ON standards. 50ef62fef3SRichard Lowe 51ef62fef3SRichard Lowe Comments must be a list of one-line comments, with no trailing 52ef62fef3SRichard Lowe newline. 53ef62fef3SRichard Lowe 54*2f54b716SRichard Lowe If check_db is True (the default), validate bug synopses against the 55*2f54b716SRichard Lowe databases. 56ef62fef3SRichard Lowe 57ef62fef3SRichard Lowe Error messages intended for the user are written to output, 58ef62fef3SRichard Lowe which defaults to stderr 59ef62fef3SRichard Lowe ''' 60*2f54b716SRichard Lowe bugnospcre = re.compile(r'^(\d{2,7})([^ ].*)') 61*2f54b716SRichard Lowe ignorere = re.compile(r'^(' + 62*2f54b716SRichard Lowe r'Portions contributed by|' + 63*2f54b716SRichard Lowe r'Contributed by|' + 64*2f54b716SRichard Lowe r'Reviewed[ -]by|' + 65*2f54b716SRichard Lowe r'Approved[ -]by|' + 66*2f54b716SRichard Lowe r'back[ -]?out)' + 67*2f54b716SRichard Lowe r'[: ]') 68cdf0c1d5Smjnelson 69cdf0c1d5Smjnelson errors = { 'bugnospc': [], 70cdf0c1d5Smjnelson 'mutant': [], 71cdf0c1d5Smjnelson 'dup': [], 72cdf0c1d5Smjnelson 'nomatch': [], 73cdf0c1d5Smjnelson 'nonexistent': [] } 74cdf0c1d5Smjnelson bugs = {} 75ef62fef3SRichard Lowe ret = 0 76ef62fef3SRichard Lowe blanks = False 77cdf0c1d5Smjnelson 78cdf0c1d5Smjnelson for com in comments: 79ef62fef3SRichard Lowe # Our input must be newline-free, comments are line-wise. 80ef62fef3SRichard Lowe if com.find('\n') != -1: 81ef62fef3SRichard Lowe raise ValueError("newline in comment '%s'" % com) 82ef62fef3SRichard Lowe 83cdf0c1d5Smjnelson # Ignore valid comments we can't check 84cdf0c1d5Smjnelson if ignorere.search(com): 85cdf0c1d5Smjnelson continue 86cdf0c1d5Smjnelson 87cdf0c1d5Smjnelson if not com or com.isspace(): 88ef62fef3SRichard Lowe blanks = True 89cdf0c1d5Smjnelson continue 90cdf0c1d5Smjnelson 91cdf0c1d5Smjnelson match = bugre.search(com) 92cdf0c1d5Smjnelson if match: 93cdf0c1d5Smjnelson if match.group(1) not in bugs: 94cdf0c1d5Smjnelson bugs[match.group(1)] = [] 95cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 96cdf0c1d5Smjnelson continue 97cdf0c1d5Smjnelson 98cdf0c1d5Smjnelson # 99cdf0c1d5Smjnelson # Bugs missing a space after the ID are still bugs 100cdf0c1d5Smjnelson # for the purposes of the duplicate ID and synopsis 101cdf0c1d5Smjnelson # checks. 102cdf0c1d5Smjnelson # 103cdf0c1d5Smjnelson match = bugnospcre.search(com) 104cdf0c1d5Smjnelson if match: 105cdf0c1d5Smjnelson if match.group(1) not in bugs: 106cdf0c1d5Smjnelson bugs[match.group(1)] = [] 107cdf0c1d5Smjnelson bugs[match.group(1)].append(match.group(2)) 108cdf0c1d5Smjnelson errors['bugnospc'].append(com) 109cdf0c1d5Smjnelson continue 110cdf0c1d5Smjnelson 111cdf0c1d5Smjnelson # Anything else is bogus 112cdf0c1d5Smjnelson errors['mutant'].append(com) 113cdf0c1d5Smjnelson 114cdf0c1d5Smjnelson if len(bugs) > 0 and check_db: 115cdf0c1d5Smjnelson bugdb = BugDB() 116cdf0c1d5Smjnelson results = bugdb.lookup(bugs.keys()) 117cdf0c1d5Smjnelson 118cdf0c1d5Smjnelson for crid, insts in bugs.iteritems(): 119cdf0c1d5Smjnelson if len(insts) > 1: 120cdf0c1d5Smjnelson errors['dup'].append(crid) 121cdf0c1d5Smjnelson 122cdf0c1d5Smjnelson if not check_db: 123cdf0c1d5Smjnelson continue 124cdf0c1d5Smjnelson 125cdf0c1d5Smjnelson if crid not in results: 126cdf0c1d5Smjnelson errors['nonexistent'].append(crid) 127cdf0c1d5Smjnelson continue 128cdf0c1d5Smjnelson 129ef62fef3SRichard Lowe # 130ef62fef3SRichard Lowe # For each synopsis, compare the real synopsis with 131ef62fef3SRichard Lowe # that in the comments, allowing for possible '(fix 132ef62fef3SRichard Lowe # stuff)'-like trailing text 133ef62fef3SRichard Lowe # 134cdf0c1d5Smjnelson for entered in insts: 135cdf0c1d5Smjnelson synopsis = results[crid]["synopsis"] 136ef62fef3SRichard Lowe if not re.search(r'^' + re.escape(synopsis) + 137cdf0c1d5Smjnelson r'( \([^)]+\))?$', entered): 138cdf0c1d5Smjnelson errors['nomatch'].append([crid, synopsis, 139cdf0c1d5Smjnelson entered]) 140cdf0c1d5Smjnelson 141cdf0c1d5Smjnelson 142cdf0c1d5Smjnelson if blanks: 143cdf0c1d5Smjnelson output.write("WARNING: Blank line(s) in comments\n") 144cdf0c1d5Smjnelson ret = 1 145cdf0c1d5Smjnelson 146cdf0c1d5Smjnelson if errors['dup']: 147cdf0c1d5Smjnelson ret = 1 148cdf0c1d5Smjnelson output.write("These IDs appear more than once in your " 149cdf0c1d5Smjnelson "comments:\n") 150cdf0c1d5Smjnelson for err in errors['dup']: 151cdf0c1d5Smjnelson output.write(" %s\n" % err) 152cdf0c1d5Smjnelson 153cdf0c1d5Smjnelson if errors['bugnospc']: 154cdf0c1d5Smjnelson ret = 1 155cdf0c1d5Smjnelson output.write("These bugs are missing a single space following " 156cdf0c1d5Smjnelson "the ID:\n") 157cdf0c1d5Smjnelson for com in errors['bugnospc']: 158cdf0c1d5Smjnelson output.write(" %s\n" % com) 159cdf0c1d5Smjnelson 160cdf0c1d5Smjnelson if errors['mutant']: 161cdf0c1d5Smjnelson ret = 1 162*2f54b716SRichard Lowe output.write("These comments are not valid bugs:\n") 163cdf0c1d5Smjnelson for com in errors['mutant']: 164cdf0c1d5Smjnelson output.write(" %s\n" % com) 165cdf0c1d5Smjnelson 166cdf0c1d5Smjnelson if errors['nonexistent']: 167cdf0c1d5Smjnelson ret = 1 168*2f54b716SRichard Lowe output.write("These bugs were not found in the databases:\n") 169cdf0c1d5Smjnelson for id in errors['nonexistent']: 170cdf0c1d5Smjnelson output.write(" %s\n" % id) 171cdf0c1d5Smjnelson 172cdf0c1d5Smjnelson if errors['nomatch']: 173cdf0c1d5Smjnelson ret = 1 174*2f54b716SRichard Lowe output.write("These bug synopses don't match " 175cdf0c1d5Smjnelson "the database entries:\n") 176cdf0c1d5Smjnelson for err in errors['nomatch']: 177*2f54b716SRichard Lowe output.write("Synopsis of %s is wrong:\n" % err[0]) 178cdf0c1d5Smjnelson output.write(" should be: '%s'\n" % err[1]) 179cdf0c1d5Smjnelson output.write(" is: '%s'\n" % err[2]) 180cdf0c1d5Smjnelson 181cdf0c1d5Smjnelson return ret 182