Ticket #245: metrics.patch
File metrics.patch, 51.9 KB (added by hopscc, 14 years ago) |
---|
-
Source/Phases/MetricsPhase.cobra
1 class MetricsPhase inherits Phase 2 """ 3 Run any Code Metrics accumulation or calculation that can be done on the AST. 4 Uses a Visitor subclass MetricsGenerator 5 """ 6 7 cue init(c as Compiler) 8 base.init(c) 9 10 get description as String is override 11 return 'Calculating Code Metrics' 12 13 def innerRun is override 14 c = .compiler 15 16 if not c.options.containsKey('metrics') # metrics calc not enabled 17 return 18 19 count = 0 20 for mod in c.modules, if mod inherits CobraModule, count += 1 21 if count == 0, return # probably all sharp modules 22 23 MetricsGeneratorVisitor().gen(c) 24 -
Source/BackEndClr/SharpGenerator.cobra
72 72 _modules.add(SharpModule(fileName, _verbosity)) 73 73 74 74 def writeSharpRunAllTests(cw as CurlyWriter) 75 runner = .options ['test-runner']to String # ex: Cobra.Lang.CobraCore.runAllTests, ex: MyProgram.runTests75 runner = .options.getSubOpt('testifyArgs', 'runner') to String # ex: Cobra.Lang.CobraCore.runAllTests, ex: MyProgram.runTests 76 76 if runner <> 'nil' 77 77 if runner.endsWith('()'), runner = runner[:-2] 78 78 if runner.startsWith('Cobra.Lang.'), runner = 'CobraLangInternal.' + runner['Cobra.Lang.'.length:] -
Source/files-to-compile.text
35 35 DocGenerator 36 36 SyntaxHighlighter 37 37 38 MetricsGenerator 39 38 40 Phases/Phase.cobra 39 41 Phases/BindRunTimeLibraryPhase.cobra 40 42 Phases/ReadLibrariesPhase.cobra … … 47 49 Phases/BindImplementationPhase.cobra 48 50 Phases/CountNodesPhase.cobra 49 51 Phases/SuggestDefaultNumberPhase.cobra 52 Phases/MetricsPhase.cobra 50 53 51 54 BackEndCommon/CurlyWriter 52 55 BackEndCommon/CurlyGenerator -
Source/MetricsGenerator.cobra
1 """ 2 Runs in metrics phase if enabled, Walks AST and generates various code metrics 3 ToDate: LinesOfCode and a simple Mccabe/Cyclomatic Complexity Calculation. 4 """ 5 6 7 use System.Reflection 8 9 class MethodMetricsAccumulator 10 """Value Object for accumulating metrics per Method""" 11 var name = '' 12 var mcc = 0 # mccabe Complexity calc accumulator 13 14 15 class MetricsGeneratorVisitor inherits Visitor 16 """ 17 Example invocations: 18 19 cobra -metrics Utils.cobra 20 cobra -metrics:loc -files:files-to-compile.text 21 cobra -metrics:mcc=10,loc=50 -lib:System.Web 22 23 Potential options: 24 * warnings vs Table 25 26 Also, see TODO comments in the code. 27 """ 28 29 var methodAccStack = Stack<of MethodMetricsAccumulator>() 30 31 # Threshold values for emitting message, set from optionValues 32 var locThreshold = 0 33 var mccThreshold = 0 34 35 var warn = false 36 var metOpt as OptionValues? # commandline optionValues for accessing metricsOptions 37 var linesOut =0 38 39 cue init 40 base.init 41 42 get methodName as String is override 43 return 'gen' 44 45 ## Generate 46 47 def dispatch(obj as Object?) 48 if true 49 base.dispatch(obj) 50 else 51 try 52 base.dispatch(obj) 53 catch exc as Exception 54 while exc inherits TargetInvocationException and exc.innerException, exc = exc.innerException to ! 55 print 56 print ' *** exception for obj [obj]:' 57 print ' ### [exc.typeOf.name]: [exc.message]' 58 59 def gen(c as Compiler) 60 """ Entry point for starting Visitor scan for metric calc""" 61 62 .metOpt = c.options 63 .locThreshold = .metOpt.getSubOpt('metrics','loc', 999) 64 .mccThreshold = .metOpt.getSubOpt('metrics','mcc', 999) 65 assert .metOpt 66 # .dispatch(c.globalNS) 67 .dispatch(c.modules) 68 69 70 #def gen(obj as Object?) 71 # if obj is nil, return 72 # msg = '*** Unbound visitation for type [obj.getType]: [obj]' 73 # print msg 74 75 def gen(mod as Module) 76 # This is to capture "native" modules like SharpModule 77 pass 78 79 def gen(mod as AssemblyModule) 80 pass 81 82 def gen(mod as CobraModule) 83 try 84 for decl in mod.topNameSpace.declsInOrder 85 .dispatch(decl) 86 finally 87 pass #.finishFile 88 89 90 def gen(ns as NameSpace) 91 for decl in ns.declsInOrder, .dispatch(decl) 92 93 # Box includes all below; Class, Interface, Struct, Extension 94 def gen(box as Box) 95 .genBoxMembers(box) 96 /# 97 def gen(cl as Class) 98 # TODO: inherits, implements, generic constraints 99 .genBoxMembers(cl) 100 101 def gen(ifc as Interface) 102 # TODO: inherits, generic constraints 103 #.writeDocString(ifc.docString) 104 .genBoxMembers(ifc) 105 106 def gen(struc as Struct) 107 # TODO: implements, generic constraints 108 #.writeDocString(struc.docString) 109 .genBoxMembers(struc) 110 111 def gen(ext as Extension) 112 #.writeDocString(ext.docString) 113 .genBoxMembers(ext) 114 #/ 115 116 def genBoxMembers(box as Box) 117 typeDecls = .sorted(for decl in box.declsInOrder where decl inherits IType) 118 if typeDecls.count 119 .genBoxMembers(typeDecls) 120 121 sharedDecls = .sorted(for decl in box.declsInOrder where decl.isShared and not (decl to Object) inherits IType) 122 if sharedDecls.count 123 .genBoxMembers(sharedDecls) 124 125 nonsharedDecls = .sorted(for decl in box.declsInOrder where not decl.isShared) 126 if nonsharedDecls.count 127 .genBoxMembers(nonsharedDecls) 128 129 def genBoxMembers(decls as IList<of IBoxMember>) 130 for decl in decls 131 .dispatch(decl) 132 #for i in decls.count 133 #decl = decls[i] 134 135 def gen(method as AbstractMethod) 136 fullName = '[method.parentBox.name].[method.name]' 137 mmacc = MethodMetricsAccumulator() 138 mmacc.name = fullName 139 mmacc.mcc = 0 140 .methodAccStack.push(mmacc) 141 142 .genStatementList(method.statements) 143 #TODO requirepart, ensurepart 144 145 mmacc = .methodAccStack.pop 146 if .metOpt.hasSubOpt('metrics','loc') and method.statements.count 147 stmts = method.statements 148 startLine = stmts[0].token.lineNum 149 numLines = stmts[stmts.count-1].lastToken.lineNum - startLine + 1 150 151 if .metOpt.hasSubOpt('metrics','mcc') 152 mcc = mmacc.mcc 153 154 if mcc > .mccThreshold or numLines > .locThreshold # 10 or 25 and 100 155 # Original McCabe had count <10 (7+/-2) Great, 10-15 OK, >50 Unmaintainable 156 # later research suggests <11 Great, < 25 OK, >25 => >=30% correlation to > faults (http://www.sdtimes.com/content/article.aspx?ArticleID=31820) 157 metricsLine = StringBuilder('[fullName.padLeft(50)]\t\t') 158 if .metOpt.hasSubOpt('metrics','loc') 159 metricsLine.append('[numLines]\t') 160 if .metOpt.hasSubOpt('metrics','mcc') 161 metricsLine.append('[mcc]') 162 #if .warn 163 # if mcc > .mccThreshold 164 # _warning('Method [fullName] complexity=[mcc] McCabe Cyclomatic Complexity > [.ccMetricsThreshold].') 165 # if numLines > .locThreshold 166 # _warning('Method [fullName] LOC=[numLines]: LOC > [.locThreshold] ([stmts.count] toplevel statements).') 167 #else 168 if .linesOut == 0 169 hdrLine = StringBuilder('...METHOD...'.padLeft(50)) 170 hdrLine.append('\t\t') 171 if .metOpt.hasSubOpt('metrics','loc'), hdrLine.append('Lines\t') 172 if .metOpt.hasSubOpt('metrics','mcc'), hdrLine.append('McCabe') 173 print hdrLine.toString 174 print metricsLine.toString 175 .linesOut += 1 176 177 def genStatementList(statements as List<of Stmt>) 178 if statements.count 179 for stmt in statements 180 .dispatch(stmt) 181 182 # convenience methods for bumping stack top mccabe metrics accumulator field 183 def incMcCabe 184 .methodAccStack.peek.mcc += 1 185 186 # For metrics to date (mccabe) we are only interested in branch type statements. 187 # For anything else with statement lists or blocks we just need to dispatch on them 188 189 # Branch Statement and sub parts 190 def gen(brStmt as BranchStmt) 191 .incMcCabe 192 #.genExpr(brStmt.expr) 193 for onPart in brStmt.onParts 194 .dispatch(onPart) 195 if brStmt.elsePart 196 .dispatch(brStmt.elsePart) 197 198 def gen(onPart as BranchOnPart) 199 #for expr in onPart.exprs 200 # .genExpr(expr) 201 .dispatch(onPart.block) 202 203 def gen(expct as ExpectStmt) 204 .dispatch(expct.block) 205 206 def gen(forStmt as ForStmt) 207 .incMcCabe 208 .dispatch(forStmt.block) 209 210 def gen(ifStmt as IfStmt) 211 .incMcCabe 212 #.genExpr(ifStmt.cond) 213 # TODO possibly increment again for each and/or in condition list 214 .dispatch(ifStmt.trueStmts) 215 if ifStmt.falseStmts 216 .dispatch(ifStmt.falseStmts) 217 218 def gen(l as LockStmt) 219 #.genExpr(l.expr) 220 .dispatch(l.block) 221 222 def gen(prtStmt as PrintRedirectStmt) 223 .dispatch(prtStmt.block) 224 225 def gen(tryStmt as TryStmt) 226 .dispatch(tryStmt.tryBlock) 227 for cb in tryStmt.catchBlocks 228 .dispatch(cb) 229 if tryStmt.successBlock 230 .dispatch(tryStmt.successBlock) 231 if tryStmt.finallyBlock 232 .dispatch(tryStmt.finallyBlock) 233 234 def gen(cb as CatchBlock) 235 .dispatch(cb.block) 236 237 def gen(us as UsingStmt) 238 #.genExpr(us.varExpr) # UsingStmt needs property for varExpr 239 #.genExpr(us.initExpr) 240 .dispatch(us.block) 241 242 def gen(aws as AbstractWhileStmt) 243 .incMcCabe 244 #.genExpr(aws.expr) 245 # TODO possibly increment again for each and/or in condition list 246 .dispatch(aws.block) 247 248 def gen(block as BlockStmt) 249 for stmt in block.stmts 250 .dispatch(stmt) 251 252 #Expressions 253 def genExpr(expr as Expr) 254 pass 255 #.dispatch(expr) 256 257 258 # catch everything else and ignore 259 def gen(node as INode) 260 pass 261 262 263 ## Sorting members 264 265 def sorted(decls as IList<of IBoxMember>) as List<of IBoxMember> 266 """ 267 Return the box members in a logically sorted order. 268 First order is by type of member (enums, vars, constructors, props, indexers, methods). 269 Second order is alphabetical. 270 Third order is parameter count. 271 """ 272 t = List<of IBoxMember>(decls) 273 t.sort(ref .compareMembers) 274 return t 275 276 def compareMembers(a as IBoxMember, b as IBoxMember) as int is shared 277 if a.getType is b.getType 278 diff = a.name.compareTo(b.name) # note that Cobra disallows member names that differ only by case 279 if diff == 0 280 if a inherits AbstractMethod 281 diff = a.params.count.compareTo((b to AbstractMethod).params.count) 282 else if a inherits Indexer 283 # b must also be an indexer since their names were the same 284 diff = a.params.count.compareTo((b to Indexer).params.count) 285 else if a.name.startsWith('_') and not b.name.startsWith('_') 286 diff = .compareNamesWithFirstUnderscored(a.name, b.name) 287 else if not a.name.startsWith('_') and b.name.startsWith('_') 288 diff = -1 * .compareNamesWithFirstUnderscored(b.name, a.name) 289 else 290 diff = .rank(a).compareTo(.rank(b)) 291 return diff 292 293 def compareNamesWithFirstUnderscored(a as String, b as String) as int is shared 294 require 295 a.startsWith('_') 296 not b.startsWith('_') 297 body 298 a = a[1:] 299 if a == b, return 1 # underscored/protected member comes last 300 else, return a.compareTo(b) 301 302 def rank(obj as IBoxMember) as int is shared 303 if obj inherits EnumDecl, return 0 304 if obj inherits MethodSig, return 10 305 if obj inherits Box, return 20 306 if obj inherits BoxConst, return 25 307 if obj inherits BoxVar, return 30 308 if obj inherits Initializer, return 40 309 if obj inherits BoxEvent, return 45 310 if obj inherits Property, return 50 311 if obj inherits Indexer, return 60 312 if obj inherits Method, return 70 313 throw FallThroughException(obj) -
Source/Statements.cobra
199 199 _onParts = onParts 200 200 _elsePart = elsePart 201 201 202 get lastToken as IToken is override 203 if _elsePart, return _elsePart.lastToken 204 return _onParts[_onParts.count-1].lastToken # _onParts[[-1]].lastToken 205 202 206 def addSubFields 203 207 base.addSubFields 204 208 .addField('expr', .expr) … … 232 236 _exprs = exprs 233 237 _block = block 234 238 239 get lastToken as IToken 240 return _block.lastToken 241 235 242 def addSubFields 236 243 base.addSubFields 237 244 .addField('exprs', _exprs) -
Source/Compiler.cobra
57 57 """ 58 58 Return binary output name for compilation of files for this backend. 59 59 """ 60 61 class NilBackEnd inherits BackEnd 62 """Null BackEnd Implementation for suppressing any BackEnd processing.""" 63 64 cue init(c as Compiler) 65 base.init(c) 60 66 67 def makePhases(phases as IList<of Phase>) is override 68 pass 61 69 70 def computeOutName as String is override 71 return .compiler.computeOutNameSharp 72 73 62 74 class Compiler implements ITypeProvider, IWarningRecorder, IErrorRecorder, ICompilerForNodes is partial 63 75 """ 64 76 General notes: … … 297 319 .backEnd.makePhases(phases) 298 320 if .options.boolValue('timeit') 299 321 phases.add(CountNodesPhase(this)) 322 if .options.containsKey('metrics') 323 phases.add(MetricsPhase(this)) 300 324 for i, phase in phases.numbered, phase.stableOrder = i 301 325 phases.sort # see Phase.order and .compareTo 302 326 return phases … … 1417 1442 on 'clr', _backEnd = ClrBackEnd(this) 1418 1443 on 'jvm', _backEnd = JvmBackEnd(this) 1419 1444 on 'objc', _backEnd = ObjcBackEnd(this) 1445 on 'nil', _backEnd = NilBackEnd(this) # Do FrontEnd execution only 1420 1446 else, throw FallThroughException(.options.get('back-end')) 1421 1447 1422 1448 -
Source/CommandLine.cobra
34 34 35 35 get type as String 36 36 require .isUnpacked 37 ensure result in ['accumulator', 'args-list', 'bool', 'int', 'menu', 'set', 'string' ]37 ensure result in ['accumulator', 'args-list', 'bool', 'int', 'menu', 'set', 'string', 'multival'] 38 38 return _type 39 39 40 40 get synonyms from var … … 77 77 if _type == 'main' 78 78 _isMain, _type = true, 'bool' 79 79 if .containsKey('is-main'), _isMain = this['is-main'] to bool 80 assert _type in ['accumulator', 'args-list', 'bool', 'int', 'menu', 'set', 'string' ]80 assert _type in ['accumulator', 'args-list', 'bool', 'int', 'menu', 'set', 'string', 'multival'] 81 81 if .containsKey('synonyms') 82 82 for syn in this['synonyms'] 83 83 _synonyms.add(syn) … … 94 94 _unpackPlatforms 95 95 _isUnpacked = true 96 96 assert .type=='menu' implies .choices.count > 0 97 assert .choices.count <> 0 implies .type in ['menu', 'set'] 97 assert .choices.count <> 0 implies .type in ['menu', 'set', 'multival'] 98 assert .type=='multival' implies .containsKey('subOptions') and .containsKey('choices') 99 assert .type=='multival' implies this['subOptions'].specsList.count == this['choices'].count 100 assert .type=='multival' implies .choices.count > 0 98 101 99 102 def _unpackPlatforms 100 103 if not .containsKey('platforms') … … 103 106 assert platform in ['jvm', '.net', 'objc'] 104 107 this['platforms'] = Set<of String>(this['platforms'] to List<of String>) 105 108 109 def defaultIfOn as dynamic 110 if .containsKey('defaultIfOn'), return this['defaultIfOn'] 111 if .containsKey('default'), return this['default'] 112 return 'on' 113 114 def subOptions as List<of OptionSpec> 115 """Return SubOption list ( of OptionSpec) or empty list if no subOptions""" 116 if .containsKey('subOptions') 117 clos = this['subOptions'] 118 assert clos inherits CommandLineOptionSpecs 119 return clos.specsList 120 return List<of OptionSpec>() 121 # TODO: cache this in unpack and just return cached value from here 106 122 123 def subOptionValue(name as String, keyList as vari String) as dynamic 124 """ 125 Find subOptions spec with given name; using keys from keyList return the value for 126 the first existing key in spec. 127 Return empty string if no subOptions or no matching keys found. 128 """ 129 for subOpt in .subOptions 130 if subOpt['name'] == name 131 for key in keyList 132 if subOpt.containsKey(key), return subOpt[key] 133 break 134 return '' 135 136 def subOptionSpec(name as String) as OptionSpec 137 """Return subOption for name or fail""" 138 require .containsKey('subOptions') 139 for subOpt in .subOptions 140 if subOpt['name'] == name 141 return subOpt 142 assert false, name 143 return OptionSpec() 144 107 145 class CommandLineOptionSpecs 108 146 """ 109 147 Returns the command line option specifications, via .specsList, as a List of OptionSpec. … … 114 152 115 153 cue init 116 154 base.init 117 for rawSpec in _rawCommandLineOptionSpecs 155 __initInternal(_rawCommandLineOptionSpecs) 156 157 cue init(rawOptionSpecs) 158 base.init 159 __initInternal(rawOptionSpecs) 160 161 def __initInternal(rawOptionSpecs) 162 assert rawOptionSpecs.count 163 for rawSpec in rawOptionSpecs 118 164 spec = OptionSpec() 119 165 if rawSpec inherits Dictionary<of String, Object> 120 for key in rawSpec.keys, spec[key] = rawSpec[key] 166 for key in rawSpec.keys 167 spec[key] = if(key == 'subOptions', CommandLineOptionSpecs(rawSpec[key]), rawSpec[key]) 121 168 else if rawSpec inherits Dictionary<of String, String> 122 169 for key in rawSpec.keys, spec[key] = rawSpec[key] 123 170 else 124 171 throw FallThroughException(rawSpec.getType) 125 172 spec.unpack 126 173 _specs.add(spec) 127 174 128 175 def specsList as List<of OptionSpec> 129 176 """ 130 177 Returns the option specs in the order they were declared. 131 178 """ 132 179 return _specs 133 180 181 # Spec for each cobra compiler commandline option - List of Dictionaries 182 # TODO: doc the types 183 #type: main 184 #type: string 185 #type: int 186 #type: bool 187 #type: set 188 #type: menu 189 # 190 # type: multival supports a single cmdline option supporting multiple subOptions as comma separated name[=value] pairs 191 # e.g. -multiValueDefault -multival:key0,key1=15,key2=fred 192 # Uses standard entries 193 # (e.g. 'default' for a default value (commaSep list of pairs) if the cmdline option not given) 194 # On the cmdline you can specify just the option (without any subOptions - to enable a default set) 195 # this uses the value of entry 'defaultIfOn' (or 'default' if non existent.) 196 # Must have a 'choices' entry which is a list of the recognised subOption names AND 197 # Must have a 'subOptions' entry whose contents is a list of Dictionaries, one for each subOption supported 198 # Each subOption dict must have at least entry for 'name' but more commonly also 199 # 'type', 'description' (for help display) and possibly entries for defaults 200 # 'defaultIfKeyOnly' and 'default' 201 # On cmdline can give just a subOption( without '=value'). If want to support this and its different from 202 # a value for 'default' (below) provide an entry for 'defaultIfKeyOnly' giving the value for this case. 203 # If want a subOption value set if the option is enabled regardless of the subOption being given 204 # then provide a 'default' entry. 205 # 134 206 var _rawCommandLineOptionSpecs = [ 135 207 { 136 208 'name': 'about', … … 332 404 'args': 'TYPENAME', 333 405 }, 334 406 { 407 'name': 'metrics', 408 'type': 'multival', 409 'args': r'[loc[=value],][mcc[=value]...]', 410 'description': 'Enable metrics calculations; name specific metrics to be done and any threshold values as comma separated name=value pairs.', 411 'choices': ['loc', 'mcc'], #allowable subOption keys 412 'example': ['loc=80,mcc=10', 'mcc,loc=50', 'mcc=12', 'mcc,loc'], 413 #'default': 'loc=100,mcc=25', # default if not specify name at all (none) 414 'defaultIfOn': 'loc=100,mcc=25', # default (what subOpt values enabled) if only specify name 415 'subOptions' : [ 416 { 417 'name': 'loc', 418 'type': 'int', 419 'description': 'Show methodname and number of codelines if greater than threshold value.', 420 'defaultIfKeyOnly': '100', # value to set if only specify subOpt name 421 }, 422 { 423 'name': 'mcc', 424 'type': 'int', 425 'description': 'Show method name and McCabe Complexity Calculation value if omplexity more than threshold. Reasonable values are in range 12-30', 426 'defaultIfKeyOnly': '25', # value to set if only specify subOpt name 427 }, 428 ], 429 }, 430 { 335 431 'name': 'namespace', 336 432 'synonyms': ['name-space', 'ns'], 337 433 'type': 'string', … … 415 511 'args': '"arg1 arg2"', 416 512 }, 417 513 { 514 'name': 'parse', 515 'synonyms': ['syntax', 'p'], 516 'description': 'Parse only, no backend phases (native codegen or compilation).', 517 'type': 'main', 518 }, 519 { 418 520 'name': 'target', 419 521 'synonyms': ['t'], 420 522 'description': 'Build a specific target.', … … 429 531 'type': 'main', 430 532 }, 431 533 { 432 'name': 'test-runner',433 'type': 'string',434 'description': 'Specify the method to invoke to run the unit tests. The method must be "shared". Typically the method will make use of classes in Cobra.Lang.Test to set up and initiate the test run.',435 'default': 'Cobra.Lang.CobraCore.runAllTests',436 'args': 'QUALIFIED-METHOD-NAME|nil',437 },438 {439 534 'name': 'testify', 440 'description': ' ...',535 'description': 'Run Cobra compiler test suite', 441 536 'type': 'main', 442 537 'developer-only': true, 443 538 }, 444 539 { 445 'name': 'testify-results', 446 'description': 'The filename to write the testify results to. Progress is still written to console.', 447 'type': 'string', 448 'default': 'r-testify', 540 'name': 'testifyArgs', 541 'type': 'multival', 542 'description': 'Set names and values for controlling testify as a list of comma separated name=value pairs.', 543 'choices': ['resultsFile', 'runner', 'numThreads'], #allowable subOption keys 544 'example': ['numThreads=4,resultsFile=testResult', 'runner=Core.myTestRunner', 'numThreads=2'], 449 545 'developer-only': true, 546 'default': 'resultsFile=r-testify,runner=Cobra.Lang.CobraCore.runAllTests,numThreads=1', 547 'subOptions' : [ 548 { 549 'name': 'resultsFile', 550 'type': 'string', 551 'description': 'Filename to write the testify results to. Progress is still written to console.', 552 'default': 'r-testify', # value to default to if not specify name at all 553 #'defaultIfKeyOnly': 'r-testify', # value to set if only specify subOpt name 554 }, 555 { 556 'name': 'runner', 557 'type': 'string', 558 'description': 'method to invoke to run the unit tests. The method must be "shared". Typically the method will make use of classes in Cobra.Lang.Test to setup and initiate the test run.', 559 'default': 'Cobra.Lang.CobraCore.runAllTests', 560 }, 561 { 562 'name': 'numThreads', 563 'type': 'int', 564 'description': 'Number of threads to run the testify testsuite in.', 565 'default': '1', 566 }, 567 ], 450 568 }, 451 569 { 452 'name': 'testify-threads',453 'description': '...',454 'type': 'int',455 'developer-only': true,456 },457 {458 570 'name': 'timeit', 459 571 'description': 'Gives the total duration of running cobra (including the target program, if it is to be run). This is "wall time", not "cpu time".', 460 572 # although this option is implied by 'testify', the description does not say so, since 'testify' is a hidden option … … 488 600 'type': 'main', 489 601 }, 490 602 ] 603 491 604 492 493 605 class CommandLine 494 606 """ 495 607 The main options that control the command line's behavior are: … … 589 701 dest = _htmlWriter to TextWriter 590 702 else 591 703 dest = Console.out 592 if _htmlWriter 593 stylePath = Path.combine(Path.getDirectoryName(CobraCore.exePath), 'styles-output-html.css') 594 _htmlWriter.writeHtml('<html><head><link href="file://[stylePath]" rel=stylesheet type="text/css"></head><body>[_htmlWriter.newLine]') 704 705 _runPreamble 595 706 print to dest 596 707 paths = _pathList to ! 597 708 options = _options … … 609 720 print 'Paths:' 610 721 for path in paths 611 722 print ' [path]' 723 612 724 if options.boolValue('testify') 613 725 .doTestify(paths) 614 726 else if options.boolValue('run') … … 617 729 .doTest(paths) 618 730 else if options.boolValue('compile') 619 731 .doCompile(paths) 732 else if options.boolValue('parse') 733 .doParse(paths) 620 734 else if options.boolValue('highlight') 621 735 .doHighlight(paths) 622 736 else if options.boolValue('document') … … 635 749 .doHelp 636 750 else 637 751 .doRun(paths) 752 _runPostamble 753 754 def _runPreamble 638 755 if _htmlWriter 756 stylePath = Path.combine(Path.getDirectoryName(CobraCore.exePath), 'styles-output-html.css') 757 _htmlWriter.writeHtml('<html><head><link href="file://[stylePath]" rel=stylesheet type="text/css"></head><body>[_htmlWriter.newLine]') 758 759 def _runPostamble 760 if _htmlWriter 639 761 _htmlWriter.writeHtml('</body></html>[_htmlWriter.newLine]') 640 762 641 763 def doHighlight(paths as List<of String>) as Compiler … … 650 772 Node.setCompiler(nil) 651 773 return comp 652 774 775 def doParse(paths as List<of String>) as Compiler 776 """ 777 FrontEnd (parse) Processing only. Tokenise..Parse..Bindings. 778 No Back end processing; No codegen, no native code compilation 779 """ 780 .options['back-end'] = 'nil' 781 return .doCompile(paths, true, false, nil) 782 653 783 def doCompile(paths as List<of String>) as Compiler 654 784 return .doCompile(paths, true, false, nil) 655 785 … … 679 809 # Each phase of the compiler may throw an exception to stop compilation. 680 810 # Before doing so, it prints its errors. 681 811 assert c.errors.count>0 682 if _options.containsKey('editor') 683 spec = _options['editor'] to String? 684 else 685 spec = Environment.getEnvironmentVariable('COBRA_EDITOR') 686 if spec and spec <> '' 687 if spec.indexOf('FILE')==-1 688 .error('Missing FILE from editor spec.') 689 if spec.indexOf('LINE')==-1 690 .error('Missing LINE from editor spec.') 691 i = spec.indexOf('_') 692 if i == -1 693 i = spec.indexOf(' ') 694 if i == -1 695 .error('Missing underscore or space from editor spec.') 696 exeName = spec.substring(0, i) 697 args = spec.substring(i+1) 698 for error in c.errors 699 if error.isError and error.hasSourceSite 700 if error.fileName.trim <> '' 701 # trace error.fileName, error.lineNum 702 args = args.replace('FILE', error.fileName) 703 args = args.replace('LINE', error.lineNum.toString) 704 p = System.Diagnostics.Process() 705 p.startInfo.fileName = exeName 706 p.startInfo.arguments = args 707 p.startInfo.useShellExecute = false 708 if _verbosity >= 3 709 print 'Running: [p.startInfo.fileName] [p.startInfo.arguments]' 710 try 711 p.start 712 p.waitForExit # TODO: is this really needed? 713 catch exc as Exception 714 print 'Cannot invoke editor:' 715 print ' Command: [p.startInfo.fileName] [p.startInfo.arguments]' 716 print ' Exception: [exc]' 717 break 812 editorSpec = if(_options.containsKey('editor'), _options['editor'] to String?, Environment.getEnvironmentVariable('COBRA_EDITOR')) 813 if editorSpec and editorSpec.length 814 _editorOnErrorLines(c, editorSpec to !) 815 718 816 CobraMain.linesCompiled = c.linesCompiled 719 817 CobraMain.nodesCompiled = c.nodesCompiled 720 818 CobraMain.tokensCompiled = c.tokensCompiled 721 819 return c 722 820 821 def _editorOnErrorLines(c as Compiler, editorSpec as String) 822 if editorSpec.indexOf('FILE')==-1, .error('Missing FILE from editor spec.') 823 if editorSpec.indexOf('LINE')==-1, .error('Missing LINE from editor spec.') 824 i = editorSpec.indexOf('_') 825 if i == -1 826 i = editorSpec.indexOf(' ') 827 if i == -1, .error('Missing underscore or space from editor spec.') 828 829 editorExe = editorSpec.substring(0, i) 830 args = editorSpec.substring(i+1) 831 for error in c.errors 832 if error.isError and error.hasSourceSite 833 if error.fileName.trim <> '' 834 # trace error.fileName, error.lineNum 835 args = args.replace('FILE', error.fileName) 836 args = args.replace('LINE', error.lineNum.toString) 837 p = System.Diagnostics.Process() 838 p.startInfo.fileName = editorExe 839 p.startInfo.arguments = args 840 p.startInfo.useShellExecute = false 841 if _verbosity >= 3 842 print 'Running: [p.startInfo.fileName] [p.startInfo.arguments]' 843 try 844 p.start 845 p.waitForExit # TODO: is this really needed? 846 catch exc as Exception 847 print 'Cannot invoke editor:' 848 print ' Command: [p.startInfo.fileName] [p.startInfo.arguments]' 849 print ' Exception: [exc]' 850 break 851 723 852 def doDocument(paths as List<of String>) as Compiler 724 853 comp = .doCompile(paths, false, false, do(c as Compiler)=c.lastPhase inherits BindInterfacePhase) 725 854 GenerateHtmlDocVisitor(do(module)=module inherits CobraModule).gen(comp) … … 799 928 800 929 exeFileName as String? = nil 801 930 runArgs = .options.getStringList('run-args') 802 # TODO: what's this?931 # TODO: exeArgs as arglist of Strings exe-name exe-arg-0 exe-arg-1 ... cf argv list 803 932 # exeArgs = .options.getDefaultLOStr('exe-args') 804 933 # if exeArgs.count 805 934 # exeFileName = exeArgs[0] … … 835 964 print ' * commands that operate on path(s) are:' 836 965 print ' -compile .... Compile only. Also, -c' 837 966 print ' -run ........ Run the program (compile if necessary). Also -r (Default)' 838 print ' -test ....... Run the unit tests of a library.'967 print ' -test ....... Run the unit tests of an app or library.' 839 968 print ' -document ... Document the program (partial compilation). Also, -doc' 840 969 print ' -highlight .. Syntax highlight the program in HTML.' 970 print ' -parse .... Parse only. No BackEnd processing. Also, -syntax, -p' 971 if Utils.isDevMachine 972 print ' -testify .... Run the compiler testsuite (cwd must be cobra Source dir).' 841 973 print '' 842 974 print ' cobra <options> <command>' 843 975 print ' * standalone commands are:' … … 877 1009 sep = ', ' 878 1010 print 879 1011 s = spec.description 880 while s.length 881 if s.length < width 882 print '[leftMarginStr][s]' 883 s = '' 884 else 885 # TODO: bug in here for narrow widths. try "width = 20" to reproduce 886 j = width + 1 887 if j >= s.length, j = s.length - 1 888 while j > 0 and s[j] <> ' ', j -= 1 889 if j 890 sub = s.substring(0, j) 891 s = if(s.length, s.substring(j+1), '') 892 print '[leftMarginStr][sub]' 1012 if s.length 1013 _printWrapped(s, width, leftMarginStr) 1014 for subOptionSpec in spec.subOptions 1015 assert subOptionSpec.name.length 1016 if subOptionSpec.containsKey('description') 1017 _printWrapped('"[subOptionSpec.name]" = [subOptionSpec.description]' , width, leftMarginStr + ' ') 1018 if subOptionSpec.hasDefault 1019 soDefault = subOptionSpec['default'] 1020 print '[leftMarginStr] (default is [soDefault])' 893 1021 if spec.containsKey('example') 894 1022 if spec['example'] inherits System.Collections.IList 895 1023 first = true … … 903 1031 if spec.containsKey('eg') # verbatim example line 904 1032 print '[leftMarginStr]e.g. [spec["eg"]]' 905 1033 1034 def _printWrapped(s as String, width as int, leftMarginStr as String) 1035 while s.length 1036 if s.length < width 1037 print '[leftMarginStr][s]' 1038 s ='' 1039 else 1040 # TODO: bug in here for narrow widths. try "width = 20" to reproduce 1041 j = width + 1 1042 if j >= s.length, j = s.length - 1 1043 while j > 0 and s[j] <> ' ', j -= 1 1044 if j 1045 sub = s.substring(0, j) 1046 s = if(s.length, s.substring(j+1), '') 1047 print '[leftMarginStr][sub]' 1048 906 1049 def doAbout 907 1050 # CC: multiline string 908 1051 print … … 1257 1400 1258 1401 def _addInDefaults(valueDict as Dictionary<of String, Object>) 1259 1402 for spec in _optionSpecs 1403 if spec.containsKey('subOptions') and valueDict.containsKey(spec.name) 1404 # set defaults for unspecified subOptions with defaults if any subOption is set 1405 _addInSubOptionDefaults(spec, valueDict) 1406 1260 1407 if not valueDict.containsKey(spec.name) and spec.hasDefault 1261 1408 defaultValue = _interpretValue(spec.default, spec) to ! 1262 1409 if .verbosity 1263 1410 print 'Setting option "[spec.name]" to default value [defaultValue].' 1264 1411 valueDict[spec.name] = defaultValue 1265 1412 1413 def _addInSubOptionDefaults(spec as OptionSpec, valueDict as Dictionary<of String, Object>) 1414 ov = valueDict[spec.name] to OptionValues # that whats been explicitly set 1415 for subOptSpec in spec.subOptions 1416 if subOptSpec.hasDefault 1417 if not ov.containsKey(subOptSpec.name) # value not set already 1418 defaultValue = _interpretValue(subOptSpec['default'], subOptSpec) to ! 1419 # Should we force subOptions to always have a default value ?? 1420 if .verbosity 1421 print 'Setting option "[spec.name]:[subOptSpec.name]" to default value [defaultValue].' 1422 ov[subOptSpec.name] = defaultValue 1423 1424 1266 1425 def _unpackOptions(valueDict as Dictionary<of String, Object>, fileList as List<of String>) 1267 1426 """ 1268 1427 Unpack certain options (verbosity and timeit) into specific class fields, … … 1381 1540 else 1382 1541 valueSet.add(choice) 1383 1542 value = valueSet 1543 on 'multival' # comma separated key=value pairs - key1[=value],key2[=value],... 1544 if valueStr =='on', valueStr = spec.defaultIfOn 1545 pairs = List<of String>(valueStr.split(c',')) 1546 assert pairs.count > 0 1547 subValueOpts = OptionValues() 1548 for kvp in pairs 1549 kve = List<of String>(kvp.trim.split(c'=')) 1550 assert kve.count > 0 1551 key = kve[0] 1552 if not key in spec.choices and key <> 'on' 1553 print 'Unknown subOption key "[key]" in option "[spec.name]"' 1554 continue 1555 valStr = if(kve.count > 1, kve[1], spec.subOptionValue(key, 'defaultIfKeyOnly', 'default') to String) 1556 iValue = _interpretValue(valStr, spec.subOptionSpec(key)) 1557 subValueOpts.setKV(key, iValue) 1558 value = subValueOpts 1384 1559 else 1385 1560 throw FallThroughException(spec.type) 1386 1561 return value … … 1428 1603 1429 1604 1430 1605 class OptionValues inherits Dictionary<of String, Object> 1431 1606 1432 1607 var _isSpecified = Dictionary<of String, bool>() # CC: could just be a Set 1433 1608 1434 1609 cue init … … 1460 1635 1461 1636 def get(key as String) as dynamic? 1462 1637 return this[key] 1463 1638 1639 def setKV(key as String, value as Object) 1640 this[key] = value 1641 .didSpecify(key) 1642 1464 1643 def getDefault(key as String, default as dynamic?) as dynamic? 1465 1644 if .containsKey(key) 1466 1645 return this[key] … … 1477 1656 else 1478 1657 return List<of String>() 1479 1658 1659 def hasSubOpt(majorKey as String, minorKey as String) as bool 1660 """Return bool indicating existance of given Option and subOption""" 1661 if not .containsKey(majorKey) 1662 return false 1663 so = .get(majorKey) to OptionValues 1664 return so.containsKey(minorKey) 1665 1666 def getSubOpt(majorKey as String, minorKey as String, default as dynamic) as dynamic 1667 """ 1668 Return subOption value for the given option and subOption (for a multi key=value Option) 1669 SubOptions are stored themselves as a set of OptionValues. 1670 If no major or minor key value return default. 1671 """ 1672 if .containsKey(majorKey) 1673 so = .get(majorKey) to OptionValues 1674 if so.containsKey(minorKey) 1675 return so[minorKey] 1676 return default 1677 1678 def getSubOpt(majorKey as String, minorKey as String ) as dynamic 1679 """ 1680 Return subOption value for the given option and subOption (multivalued option, multi key=value). 1681 Both the Option and subOption must exist otherwise an assert exception is incurred 1682 """ 1683 assert .containsKey(majorKey) 1684 so = .get(majorKey) to OptionValues 1685 assert so.containsKey(minorKey) 1686 return so[minorKey] 1687 1480 1688 # CC: def getDefault<of T>(key as String, value as T) as T ... 1481 1689 1482 1690 def setValue(key as String) as Set<of String> -
Source/TestifyRunner.cobra
58 58 paths = _pathList 59 59 if paths.count == 0 60 60 paths = .cobraTestPaths + [Path.getFullPath(Path.combine('..', 'HowTo'))] 61 numThreads = .options.getDefault('testify-threads', 1) to int 61 #numThreads = .options.getDefault('testify-threads', 1) to int 62 numThreads = .options.getSubOpt('testifyArgs', 'numThreads', 1) to int 62 63 if numThreads > 1 63 64 .runThreaded(numThreads, paths) 64 65 else … … 77 78 78 79 args = CobraCore.commandLineArgs 79 80 _subCobraExe = args[0] 80 _subCommandLineArgs = for arg in args[1:] where arg.startsWith('-') and not '-testify -threads:' in arg81 _subCommandLineArgs = for arg in args[1:] where arg.startsWith('-') and not '-testifyArgs:' in arg 81 82 82 83 _statusWriter.writeLine('Queueing for threads:') 83 84 for path in paths … … 102 103 concat.append(sep) 103 104 concat.append(File.readAllText(fileName)) 104 105 sep = sepNl 105 File.writeAllText(.options ['testify-results']to String, concat.toString)106 File.writeAllText(.options.getSubOpt('testifyArgs','resultsFile') to String, concat.toString) 106 107 for fileName in _subResultsFileNames 107 108 try 108 109 File.delete(fileName) … … 110 111 _statusWriter.writeLine('warning: Cannot delete "[fileName]" due to: [exc]') 111 112 112 113 def _printTotals 113 resultsFileName = .options ['testify-results']to String114 resultsFileName = .options.getSubOpt('testifyArgs', 'resultsFile') to String 114 115 using resultsWriter = File.appendText(resultsFileName) 115 116 __printTotals(resultsWriter to !) 116 117 __printTotals(_statusWriter to !) … … 133 134 pathIndex = _subDirQueue.count 134 135 resultsFileName = 'r-testify-[pathIndex]' 135 136 lock _subResultsFileNames, _subResultsFileNames[pathIndex] = resultsFileName 136 args = _subCommandLineArgs + ['-testify -results:[resultsFileName]', '-testify-threads:1', '"[path]"']137 args = _subCommandLineArgs + ['-testifyArgs:resultsFile=[resultsFileName],numThreads=1', '"[path]"'] 137 138 lock _statusWriter, _statusWriter.writeLine('Thread [tid] start: [args.join(" ")]') 138 139 p = Process() 139 140 p.startInfo.useShellExecute = false … … 177 178 _statusWriter = IndentedWriter(AutoFlushWriter(Console.out)) 178 179 _statusWriter.indentString = ' ' 179 180 try 180 resultsFileName = .options ['testify-results']to String181 resultsFileName = .options.getSubOpt('testifyArgs', 'resultsFile') to String 181 182 using resultsWriter = File.createText(resultsFileName) 182 183 _resultsWriter = IndentedWriter(AutoFlushWriter(resultsWriter)) 183 184 print to _resultsWriter to ! -
Tests/700-command-line/912-parse-only.cobra
1 # Test parse only + output metrics info (No native compile or run) 2 use System.Text.RegularExpressions 3 4 class Test 5 6 def main is shared 7 cobraPath = CobraCore.findCobraExe to ! 8 if 'Snapshot' in cobraPath 9 # TODO 2008-10-19: Not sure why this is happening yet. 10 print 'WARNING: Snapshot in cobra path:', cobraPath 11 cobraPath = cobraPath.replace('\\Snapshot\\', '\\') 12 cobraPath = cobraPath.replace('/Snapshot/', '/') 13 src = '911-metrics-fodder.cobra' 14 exe = '911-metrics-fodder.exe' 15 try 16 File.delete(exe) 17 catch 18 pass 19 .parseOnly(cobraPath, '-parse [src]') 20 assert not File.exists(exe) 21 22 #default metrics mccabe >25 23 .metrics1(cobraPath, '-syntax -metrics [src]') 24 25 #metrics explicit mccabe >10, loc >20 26 .metrics2(cobraPath, '-p -metrics:loc=20,mcc=10 [src]') 27 assert not File.exists(exe) 28 29 .metricsErr(cobraPath, '-p -metrics:Xmcc=20 [src]') 30 31 def parseOnly(cobraPath as String, cmdln as String)is shared 32 p as System.Diagnostics.Process? 33 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 34 #print output 35 assert 'Unhandled Exception' not in output 36 assert 'Executed' not in output 37 assert '...METHOD...\t\tLines\tMcCabe' not in output 38 assert p.exitCode == 0 39 40 #default metrics mccabe >25 41 def metrics1(cobraPath as String, cmdln as String) is shared 42 p as System.Diagnostics.Process? 43 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 44 #print output 45 assert 'Unhandled Exception' not in output 46 assert 'Executed' not in output 47 assert '...METHOD...\t\tLines\tMcCabe' in output 48 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*76\s*32') 49 assert p.exitCode == 0 50 51 #metrics explicit mccabe >1, loc >1 (all multiline methods) 52 def metrics2(cobraPath as String, cmdln as String) is shared 53 p as System.Diagnostics.Process? 54 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 55 #print output 56 assert 'Unhandled Exception' not in output 57 assert 'Executed' not in output 58 assert '...METHOD...\t\tLines\tMcCabe' in output 59 assert Regex.isMatch(output, r'MetricsTest\.tryAll\s*26\s*6') 60 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*76\s*32') 61 assert p.exitCode == 0 62 63 def metricsErr(cobraPath as String, cmdln as String) is shared 64 p as System.Diagnostics.Process? 65 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 66 #print output 67 assert 'Unhandled Exception' not in output 68 assert 'Executed' not in output 69 assert '...METHOD...\t\tLines\tMcCabe' not in output 70 assert 'Unknown subOption key "Xmcc" in option "metrics"' in output 71 assert p.exitCode == 0 72 -
Tests/700-command-line/910-metrics.cobra
1 # Test compile+output metrics info (after compile and run) 2 use System.Text.RegularExpressions 3 4 class Test 5 6 def main is shared 7 cobraPath = CobraCore.findCobraExe to ! 8 if 'Snapshot' in cobraPath 9 # TODO 2008-10-19: Not sure why this is happening yet. 10 print 'WARNING: Snapshot in cobra path:', cobraPath 11 cobraPath = cobraPath.replace('\\Snapshot\\', '\\') 12 cobraPath = cobraPath.replace('/Snapshot/', '/') 13 14 src='911-metrics-fodder.cobra' 15 .noMetrics(cobraPath, src) 16 17 #default metrics mccabe >25 18 .metrics1(cobraPath, '-metrics [src]') 19 20 #metrics explicit mccabe >1, loc >1 ( all multiline methods) 21 .metrics2(cobraPath, '-metrics:loc=1,mcc=1 [src]') 22 23 #mcc only no LOC 24 .metrics3(cobraPath, '-metrics:mcc=1 [src]') 25 26 #LOC no mcc 27 .metrics4(cobraPath, '-metrics:loc=10 [src]') 28 29 def noMetrics(cobraPath as String, cmdln as String)is shared 30 p as System.Diagnostics.Process? 31 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 32 #print output 33 assert 'Unhandled Exception' not in output 34 assert 'Executed' in output 35 assert '...METHOD...\t\tLines\tMcCabe' not in output 36 assert p.exitCode == 0 37 38 #default metrics mccabe >25 39 def metrics1(cobraPath as String, cmdln as String) is shared 40 p as System.Diagnostics.Process? 41 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 42 #print output 43 assert 'Unhandled Exception' not in output 44 assert 'Executed' in output 45 assert '...METHOD...\t\tLines\tMcCabe' in output 46 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*76\s*32') 47 assert p.exitCode == 0 48 49 #metrics explicit mccabe >1, loc >1 (all multiline methods) 50 def metrics2(cobraPath as String, cmdln as String) is shared 51 p as System.Diagnostics.Process? 52 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 53 #print output 54 assert 'Unhandled Exception' not in output 55 assert 'Executed' in output 56 assert '...METHOD...\t\tLines\tMcCabe' in output 57 assert Regex.isMatch(output, r'MetricsTest\.main\s*5\s*0') 58 assert Regex.isMatch(output, r'MetricsTest\.none\s*19\s*0') 59 assert Regex.isMatch(output, r'MetricsTest\.small\s*7\s*3') 60 assert Regex.isMatch(output, r'MetricsTest\.tryAll\s*26\s*6') 61 assert Regex.isMatch(output, r'MetricsTest\.chkBranch\s*6\s*1') 62 assert Regex.isMatch(output, r'MetricsTest\.chkFor\s*4\s*1') 63 assert Regex.isMatch(output, r'MetricsTest\.chkIf\s*3\s*1') 64 assert Regex.isMatch(output, r'MetricsTest\.chkIfElse\s*4\s*1') 65 assert Regex.isMatch(output, r'MetricsTest\.chkIfElseIf\s*5\s*2') 66 assert Regex.isMatch(output, r'MetricsTest\.chkPostWhile\s*5\s*1') 67 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*76\s*32') 68 assert p.exitCode == 0 69 70 #metrics explicit mcc only no LOC 71 def metrics3(cobraPath as String, cmdln as String) is shared 72 p as System.Diagnostics.Process? 73 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 74 #print output 75 assert 'Unhandled Exception' not in output 76 assert 'Executed' in output 77 assert '...METHOD...\t\tMcCabe' in output 78 assert Regex.isMatch(output, r'MetricsTest\.small\s*3') 79 assert Regex.isMatch(output, r'MetricsTest\.tryAll\s*6') 80 assert Regex.isMatch(output, r'MetricsTest\.chkIfElseIf\s*2') 81 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*32') 82 assert p.exitCode == 0 83 84 #metrics explicit LOC only no mcc 85 def metrics4(cobraPath as String, cmdln as String) is shared 86 p as System.Diagnostics.Process? 87 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 88 #print output 89 assert 'Unhandled Exception' not in output 90 assert 'Executed' in output 91 assert '...METHOD...\t\tLines' in output 92 assert Regex.isMatch(output, r'MetricsTest\.none\s*19') 93 assert Regex.isMatch(output, r'MetricsTest\.tryAll\s*26') 94 assert Regex.isMatch(output, r'MetricsTest\.complexExpr\s*76') 95 assert p.exitCode == 0 96 -
Tests/700-command-line/911-metrics-fodder.cobra
1 # .skip. 2 # program run to test LOC/McCabe metrics generation 3 class MetricsTest 4 def main is shared 5 a = CobraCore.commandLineArgs 6 CobraCore.noOp(a) 7 # .small(99) 8 # emit something to show compilation worked 9 print 'Executed' 10 11 # the following methods contents are nonsensical and exist only to provide 12 # examples of cobra statements that do and dont get processed for metrics 13 14 # if any of these methods change size/linecount the LOC values in 15 # 910-metrics.cobra also need to be changed 16 17 #mccabe 1 18 def chkIf(v as int) 19 if v < 1 20 return 21 print v 22 23 #mccabe 1 24 def chkIfElse(v as int) 25 if v < 1 26 return 27 else 28 print v 29 30 #mccabe 2 31 def chkIfElseIf(v as int) 32 if v < 1 33 return 34 else if v > 21 35 v = 21 36 print v 37 38 #mccabe 1 39 def chkFor(v as int) 40 j =0 41 for i in v 42 j = j +v 43 print j 44 45 #mccabe 1 46 def chkWhile(v as int) 47 i = j = 0 48 while i < v 49 j = j + v 50 i += 1 51 break 52 continue # (:-) 53 print j 54 55 #mccabe 1 56 def chkPostWhile(v as int) 57 i = j = 0 58 post while i < v 59 j = j + v 60 i += 1 61 print j 62 63 #mccabe 1 64 def chkBranch(v as int) 65 branch v 66 on 1, ones =1 67 on 11, ones =2 68 on 111, ones =3 69 else, ones =-1 70 print ones 71 72 #mccabe 3 73 def small( v as int) is shared 74 if v < 1 75 return 76 if v >10 77 print 'v over 10' 78 79 if v >1 and v < 5 80 print 'tween 1 and 5' 81 82 #mccabe 0 83 def none(v as int) as int is shared 84 assert v >0 85 try 86 v = 100-v 87 v2 = 100/v 88 catch 89 print 'caught' 90 finally 91 v += 1 92 CobraCore.noOp(v2) 93 94 trace v 95 throw ArgumentException() 96 expect ArgumentException 97 throw ArgumentException('expect') 98 #listen,ignore 99 #using, yield 100 lock Object() 101 pass 102 return v 103 104 #mccabe 6 105 def tryAll(v as int) is shared 106 if v <= 0 #1 107 return 108 else if v > 21 #2 109 v = 21 110 111 j=0 112 for i in v #3 113 j += i 114 115 v1 =v 116 while v/10 > 1 #4 117 v = v-5 118 119 v = v1 120 post while v <30 #5 121 v = v +5 122 123 v = v1 124 branch v # 6 125 on 10 126 tens='1' 127 on 20, tens='2' 128 on 30, tens='3' 129 else 130 tens='unknown' 131 print v, tens 132 133 #mccabe 32 134 def complexExpr(peek as String) as String 135 if peek=='LPAREN' 136 expr='(' 137 else if peek[0]=='DOT' 138 expr='.' 139 try 140 peek = peek[1:] 141 if peek=='ID' 142 expr = 'MemberExpr(memberToken) to Expr' 143 else if peek=='OPEN_CALL' 144 expr = '.callExpr' 145 else if peek=='OPEN_GENERIC' 146 expr = '.callExpr' 147 else 148 .throwError('Syntax error after "."') 149 finally 150 print '.opStack.pop' 151 return 'BinaryOpExpr.make' 152 else if peek=='NIL' 153 return 'NilLiteral(.grab)' 154 else if peek=='TRUE' 155 return 'BoolLit(.grab)' 156 else if peek=='FALSE' 157 return 'BoolLit(.grab)' 158 else if peek=='THIS' 159 return 'ThisLit(.grab)' 160 else if peek=='BASE' 161 return 'BaseLit(.grab)' 162 else if peek=='VAR' 163 assert peek.length #_curCodePart 164 if true #_curCodePart inherits ProperDexerXetter 165 return 'VarLit(.grab, _curCodePart)' 166 else 167 .throwError('Cannot refer to `var` in expressions outside of a property `get` or `set`.') 168 throw Exception() # stop a warning 169 else if peek=='CHAR_LIT_SINGLE' 170 return 'CharLit(.grab)' 171 else if peek=='CHAR_LIT_DOUBLE' 172 return 'CharLit(.grab)' 173 else if peek=='STRING_START_SINGLE' 174 return ".stringWithSubstitutionLit('STRING_START_SINGLE', 'STRING_PART_SINGLE', 'STRING_STOP_SINGLE')" 175 else if peek=='STRING_START_DOUBLE' 176 return ".stringWithSubstitutionLit('STRING_START_DOUBLE', 'STRING_PART_DOUBLE', 'STRING_STOP_DOUBLE')" 177 else if peek=='STRING_SINGLE' 178 return 'StringLit(.grab)' 179 else if peek=='STRING_DOUBLE' 180 return 'StringLit(.grab)' 181 else if peek=='INTEGER_LIT' 182 return 'IntegerLit(.grab)' 183 else if peek=='DECIMAL_LIT' 184 return 'DecimalLit(.grab)' 185 else if peek=='FRACTIONAL_LIT' 186 return 'FractionalLit(.grab)' 187 else if peek=='FLOAT_LIT' 188 return 'FloatLit(.grab)' 189 else if peek=='LBRACKET' 190 return '.literalList' 191 else if peek=='ARRAY_OPEN' 192 return '.literalArray' 193 else if peek=='LCURLY' 194 return '.literalDictOrSet' 195 else if peek in ['OPEN_DO', 'DO'] 196 return '.doExpr' 197 else if peek=='OPEN_IF' 198 return '.ifExpr' 199 else if peek=='FOR' 200 return '.forExpr' 201 else if peek=='OPEN_CALL' 202 return '.callExpr' 203 else if peek=='OPEN_GENERIC' 204 if true #.opStack.count and .opStack.peek == 'DOT' 205 return '.callExpr' 206 else 207 return 'TypeExpr(.typeId)' 208 else if peek=='ID' 209 return '.identifierExpr' 210 return expr 211 212 def throwError(s as String) 213 pass -
Developer/IntermediateReleaseNotes.text
64 64 65 65 * Generate warning on use of deprecated `$sharp('...')` syntax in favor of newer alternate sharp-string-literal form: `sharp'...'` and `sharp"..."`. 66 66 67 * Add support for code metrics, LOC and McCabe so far. ticket:245 67 68 69 68 70 ================================================================================ 69 71 Library 70 72 ================================================================================ … … 94 96 95 97 * Added -verbosity-ref option to output information about resolving references to libraries. 96 98 97 99 * Added support for specifying a cmdline option with subOptions and modified 100 -test-runner, -testify-Threads, -testify-results to use it becoming a new multival option 101 -testifyArgs with subOptions runner,results and numThreads. 102 e.g. -testifyArgs:results=tResults,numThreads=4,runner=Core.Runner 103 -testifyArgs:numThreads=4 104 98 105 ================================================================================ 99 106 Samples etc. 100 107 ================================================================================ … … 475 482 * Fixed: Dynamic binding cannot find `shared` methods. ticket:208 476 483 477 484 * Fixed: File and line number are duplicated in some compiler error messages. ticket:212 485 486 * Fixed: Better error message for unrecognised docString : ticket:218 487