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# 24cdf0c1d5Smjnelson# Copyright 2008 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}) (.*)$') 40*ef62fef3SRichard 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 54cdf0c1d5Smjnelsondef comchk(comments, check_db=True, output=sys.stderr): 55*ef62fef3SRichard Lowe '''Validate checkin comments against ON standards. 56*ef62fef3SRichard Lowe 57*ef62fef3SRichard Lowe Comments must be a list of one-line comments, with no trailing 58*ef62fef3SRichard Lowe newline. 59*ef62fef3SRichard Lowe 60*ef62fef3SRichard Lowe If check_db is True (the default), validate CR and ARC 61*ef62fef3SRichard Lowe synopses against the databases. 62*ef62fef3SRichard Lowe 63*ef62fef3SRichard Lowe Error messages intended for the user are written to output, 64*ef62fef3SRichard Lowe which defaults to stderr 65*ef62fef3SRichard 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 = {} 76*ef62fef3SRichard Lowe ret = 0 77*ef62fef3SRichard Lowe blanks = False 78cdf0c1d5Smjnelson 79cdf0c1d5Smjnelson for com in comments: 80*ef62fef3SRichard Lowe # Our input must be newline-free, comments are line-wise. 81*ef62fef3SRichard Lowe if com.find('\n') != -1: 82*ef62fef3SRichard Lowe raise ValueError("newline in comment '%s'" % com) 83*ef62fef3SRichard Lowe 84cdf0c1d5Smjnelson # Ignore valid comments we can't check 85cdf0c1d5Smjnelson if ignorere.search(com): 86cdf0c1d5Smjnelson continue 87cdf0c1d5Smjnelson 88cdf0c1d5Smjnelson if not com or com.isspace(): 89*ef62fef3SRichard 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: 115cdf0c1d5Smjnelson case = normalize_arc(match.group(1)) 116cdf0c1d5Smjnelson if case not in arcs: arcs[case] = [] 117cdf0c1d5Smjnelson arcs[case].append(match.group(2)) 118cdf0c1d5Smjnelson continue 119cdf0c1d5Smjnelson 120cdf0c1d5Smjnelson # Anything else is bogus 121cdf0c1d5Smjnelson errors['mutant'].append(com) 122cdf0c1d5Smjnelson 123cdf0c1d5Smjnelson if len(bugs) > 0 and check_db: 124cdf0c1d5Smjnelson bugdb = BugDB() 125cdf0c1d5Smjnelson results = bugdb.lookup(bugs.keys()) 126cdf0c1d5Smjnelson 127cdf0c1d5Smjnelson for crid, insts in bugs.iteritems(): 128cdf0c1d5Smjnelson if len(insts) > 1: 129cdf0c1d5Smjnelson errors['dup'].append(crid) 130cdf0c1d5Smjnelson 131cdf0c1d5Smjnelson if not check_db: 132cdf0c1d5Smjnelson continue 133cdf0c1d5Smjnelson 134cdf0c1d5Smjnelson if crid not in results: 135cdf0c1d5Smjnelson errors['nonexistent'].append(crid) 136cdf0c1d5Smjnelson continue 137cdf0c1d5Smjnelson 138*ef62fef3SRichard Lowe # 139*ef62fef3SRichard Lowe # For each synopsis, compare the real synopsis with 140*ef62fef3SRichard Lowe # that in the comments, allowing for possible '(fix 141*ef62fef3SRichard Lowe # stuff)'-like trailing text 142*ef62fef3SRichard Lowe # 143cdf0c1d5Smjnelson for entered in insts: 144cdf0c1d5Smjnelson synopsis = results[crid]["synopsis"] 145*ef62fef3SRichard Lowe if not re.search(r'^' + re.escape(synopsis) + 146cdf0c1d5Smjnelson r'( \([^)]+\))?$', entered): 147cdf0c1d5Smjnelson errors['nomatch'].append([crid, synopsis, 148cdf0c1d5Smjnelson entered]) 149cdf0c1d5Smjnelson 150cdf0c1d5Smjnelson for case, insts in arcs.iteritems(): 151cdf0c1d5Smjnelson if len(insts) > 1: 152cdf0c1d5Smjnelson errors['dup'].append(case) 153cdf0c1d5Smjnelson 154cdf0c1d5Smjnelson if not check_db: 155cdf0c1d5Smjnelson continue 156cdf0c1d5Smjnelson 157cdf0c1d5Smjnelson com, id = case.split(' ') 158cdf0c1d5Smjnelson arc = ARC(com, id) 159cdf0c1d5Smjnelson 160cdf0c1d5Smjnelson if not arc.valid(): 161cdf0c1d5Smjnelson errors['nonexistent'].append(case) 162cdf0c1d5Smjnelson continue 163cdf0c1d5Smjnelson 164cdf0c1d5Smjnelson # 165cdf0c1d5Smjnelson # The opensolaris.org ARC interfaces only give us the 166cdf0c1d5Smjnelson # first 40 characters of the case name, so we must limit 167cdf0c1d5Smjnelson # our checking similarly. 168cdf0c1d5Smjnelson # 169cdf0c1d5Smjnelson # We first try a direct match between the actual case name 170cdf0c1d5Smjnelson # and the entered comment. If that fails we remove a possible 171cdf0c1d5Smjnelson # trailing (fix nit)-type comment, and re-try. 172cdf0c1d5Smjnelson # 173cdf0c1d5Smjnelson for entered in insts: 174cdf0c1d5Smjnelson if entered[0:40] == arc.name(): 175cdf0c1d5Smjnelson continue 176cdf0c1d5Smjnelson else: 177*ef62fef3SRichard Lowe # Try again with trailing (fix ...) removed. 178cdf0c1d5Smjnelson dbcom = re.sub(r' \([^)]+\)$', '', entered) 179cdf0c1d5Smjnelson if dbcom[0:40] != arc.name(): 180cdf0c1d5Smjnelson errors['nomatch'].append([case, 181cdf0c1d5Smjnelson arc.name(), 182cdf0c1d5Smjnelson entered]) 183cdf0c1d5Smjnelson 184cdf0c1d5Smjnelson if blanks: 185cdf0c1d5Smjnelson output.write("WARNING: Blank line(s) in comments\n") 186cdf0c1d5Smjnelson ret = 1 187cdf0c1d5Smjnelson 188cdf0c1d5Smjnelson if errors['dup']: 189cdf0c1d5Smjnelson ret = 1 190cdf0c1d5Smjnelson output.write("These IDs appear more than once in your " 191cdf0c1d5Smjnelson "comments:\n") 192cdf0c1d5Smjnelson for err in errors['dup']: 193cdf0c1d5Smjnelson output.write(" %s\n" % err) 194cdf0c1d5Smjnelson 195cdf0c1d5Smjnelson if errors['bugnospc']: 196cdf0c1d5Smjnelson ret = 1 197cdf0c1d5Smjnelson output.write("These bugs are missing a single space following " 198cdf0c1d5Smjnelson "the ID:\n") 199cdf0c1d5Smjnelson for com in errors['bugnospc']: 200cdf0c1d5Smjnelson output.write(" %s\n" % com) 201cdf0c1d5Smjnelson 202cdf0c1d5Smjnelson if errors['mutant']: 203cdf0c1d5Smjnelson ret = 1 204cdf0c1d5Smjnelson output.write("These comments are neither bug nor ARC case:\n") 205cdf0c1d5Smjnelson for com in errors['mutant']: 206cdf0c1d5Smjnelson output.write(" %s\n" % com) 207cdf0c1d5Smjnelson 208cdf0c1d5Smjnelson if errors['nonexistent']: 209cdf0c1d5Smjnelson ret = 1 210cdf0c1d5Smjnelson output.write("These bugs/ARC cases were not found in the " 211cdf0c1d5Smjnelson "databases:\n") 212cdf0c1d5Smjnelson for id in errors['nonexistent']: 213cdf0c1d5Smjnelson output.write(" %s\n" % id) 214cdf0c1d5Smjnelson 215cdf0c1d5Smjnelson if errors['nomatch']: 216cdf0c1d5Smjnelson ret = 1 217cdf0c1d5Smjnelson output.write("These bugs/ARC case synopsis/names don't match " 218cdf0c1d5Smjnelson "the database entries:\n") 219cdf0c1d5Smjnelson for err in errors['nomatch']: 220cdf0c1d5Smjnelson output.write("Synopsis/name of %s is wrong:\n" % err[0]) 221cdf0c1d5Smjnelson output.write(" should be: '%s'\n" % err[1]) 222cdf0c1d5Smjnelson output.write(" is: '%s'\n" % err[2]) 223cdf0c1d5Smjnelson 224cdf0c1d5Smjnelson return ret 225