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