All Downloads are FREE. Search and download functionalities are using the official Maven repository.

core_analytics_quality.checksEngine.pure Maven / Gradle / Ivy

There is a newer version: 4.67.8
Show newest version
import meta::analytics::quality::model::*;
import meta::analytics::quality::model::domain::*;
import meta::relational::tests::*;
import meta::pure::runtime::*;


Enum meta::analytics::quality::model::Severity
{
   High, Medium, Low
}

Enum meta::analytics::quality::model::Category
{
   Modelling, Correctness, Quality, Testing
}

Class meta::analytics::quality::model::Rule
{
   id : String[1];
   func: FunctionDefinition<{T[1]->Any[*]}>[1];
   severity : Severity[1];
   category : Category[1];
   description: String[1];
   hasSkipTestsFlag : Boolean[1];

}

Profile meta::analytics::quality::model::rule
{
    stereotypes: [skipTests];
    tags: [rule, severity, category, description, ignore];
}

Class meta::analytics::quality::model::CheckResult
{
   isValid: Boolean[1];
   message: String[1];
}

Class meta::analytics::quality::model::ViolationInstance
{
   source : T[1];
   rule : Rule[1];
   sourceInfo() { $this.source->sourceInformation()}: SourceInformation[0..1] ;
   detail :
    CheckResult[1];
}

Class meta::analytics::quality::model::Rankings
{
   severity() { [pair(Severity.High, 1), pair(Severity.Medium, 2),pair(Severity.Low, 3)]}: Pair[*];
   severity(severity :Severity[1]) { $this.severity()->filter(p|$p.first == $severity)->toOne().second} : Integer[1];

}

function <> meta::analytics::quality::model::domain::apply(rule:Rule[1],pe:AbstractProperty[1]):Any[*]
{
  if($rule->skipInTests($pe), |[], |$rule.func->eval($pe))
}

function {doc.doc = 'Run quality checks for class properties'}
meta::analytics::quality::model::domain::runClassPropertyQualityChecks(cl:Class[1],rules:Rule>[*]):ViolationInstance[*]
{
   let properties = $cl->allProperties();

   $properties->map(p:AbstractProperty[1] | $rules->map(rule:Rule>[1] | $rule->apply($p)->cast(@CheckResult)->violationHandler($p, $rule)));
}

function <> meta::analytics::quality::model::domain::skipInTests(rule:Rule[1],pe:Any[1]):Boolean[1]
{
  if($rule.hasSkipTestsFlag ,
      |
        let pName = $pe->match([p:PackageableElement[1]|$p->fullPackageName('::'), p:AbstractProperty[1]|$p->fullPackageName('::')]);
        let si = $pe->sourceInformation();
        $pName->contains('::tests::') || $pName->contains('::test::')|| $si.source->contains('/test/') || $si.source->contains('/tests/');,
      | false
      );
}

function {doc.doc = 'Handles model checks violations'}
//TODO: there is a bug with generics so ViolationInstance instead of ViolationInstance
meta::analytics::quality::model::domain::violationHandler(detail:CheckResult[*],element: Any[1],rule: Rule[1]):ViolationInstance[*]
{
  $detail->filter(r | !$r.isValid)->map(r | ^ViolationInstance(source=$element, rule=$rule, detail=$r));
}

function meta::analytics::quality::model::domain::createRule(ruleFunctionB: FunctionDefinition<{T[1]->Any[*]}>[1]):Rule[1]
{
    let ruleFunction = $ruleFunctionB->cast(@AnnotatedElement);
    let tags = $ruleFunction.taggedValues;

    let rule = $ruleFunction->value4Tag('rule', rule).value->toOne();
    let description = $ruleFunction->value4Tag('description', rule).value->toOne();
    let severity = Severity->extractEnumValue($tags->filter(t | $t.tag == rule->tag('severity'))->map(t | $t.value)->toOne()->toString());
    let category = Category->extractEnumValue($tags->filter(t | $t.tag == rule->tag('category'))->map(t | $t.value)->toOne()->toString());
    let skipTestsTag = $ruleFunction->hasStereotype('skipTests',meta::analytics::quality::model::rule);
    ^Rule(id=$rule,func=$ruleFunctionB,severity=$severity,category=$category,description=$description,hasSkipTestsFlag=$skipTestsTag);
}

function {doc.doc='Run quality checks for all elements in a package'}
meta::analytics::quality::model::domain::runQualityChecks(elements:PackageableElement[*]):ViolationInstance[*]
{
     let functionRules = functionRules();
     let enumerationRules = enumerationRules();
     let associationRules = associationRules();
     let classRules = classRules();
     let propertyRules = propertyRules();

     let allElements = $elements->filter(e | $e->instanceOf(Package))->cast(@Package)->map(p | $p->getAllPackageElements(true))->concatenate($elements)->removeDuplicates();

     let funcViolations = $allElements->filter(x|$x->instanceOf(FunctionDefinition))->cast(@FunctionDefinition)->runQualityChecksForFunctions($functionRules);
     let classes = $allElements->filter(x|$x->instanceOf(Class))->cast(@Class);
     let associations = $allElements->filter(x|$x->instanceOf(Association));
     let classViolations = $classes->runQualityChecksForClass($classRules, $propertyRules)->concatenate($classes.qualifiedProperties->runQualityChecksForFunctions($functionRules));
     let enumerationViolations = $allElements->filter(x|$x->instanceOf(Enumeration))->cast(@Enumeration)->runQualityChecksForEnumerations($enumerationRules);
     let associationViolations = $allElements->filter(x|$x->instanceOf(Association))->cast(@Association)->runQualityChecksForAssociations($associationRules);

     $funcViolations->concatenate($classViolations)->concatenate($enumerationViolations)->concatenate($associationViolations);
}

function {doc.doc = 'Run Quality checks for functions'}
meta::analytics::quality::model::domain::runQualityChecksForFunctions(fns:FunctionDefinition[*], rules : Rule>>[*]):ViolationInstance[*]
{
   if($fns->isEmpty()
     ,|[]
     ,|$rules->map(rule|
         let ruleFunction=$rule.func->cast(@FunctionDefinition<{List>[1]->Pair[*]}>);//todo this is a hack for compile mode

         let filtersFns = $fns->filter(f|!$rule->skipInTests($f));

         let results = $ruleFunction->eval(^List>(values=$filtersFns));


         $results->map(p|
               []->concatenate($p.second)->violationHandler($p.first,$rule);
            );
        );
     );

}

function {doc.doc = 'Run quality checks for classes, their properties and abstract functions'}
meta::analytics::quality::model::domain::runQualityChecksForClass(cls:Class[*], classRules:Rule>[*], propRules:Rule>[*]):ViolationInstance[*]
{

   $cls->map(cl| $classRules->map(rule:Rule>[1] | $rule->apply($cl)->cast(@CheckResult)->violationHandler($cl, $rule))
                       ->concatenate($cl->meta::analytics::quality::model::domain::runClassPropertyQualityChecks($propRules)));
}

function {doc.doc = 'Run quality checks for enums'}
meta::analytics::quality::model::domain::runQualityChecksForEnumerations(e:Enumeration[*], enumerationRules:Rule>[*]):ViolationInstance[*]
{
   $e->map(e | $enumerationRules->map(rule:Rule>[1] | $rule->apply($e)->cast(@CheckResult)->violationHandler($e, $rule)));
}

function {doc.doc = 'Run quality checks for associations'}
meta::analytics::quality::model::domain::runQualityChecksForAssociations(e:Association[*], associationRules:Rule[*]):ViolationInstance[*]
{
   $e->map(e | $associationRules->map(rule:Rule[1] | $rule->apply($e)->cast(@CheckResult)->violationHandler($e, $rule)));
}

function <> meta::analytics::quality::model::domain::apply(rule:Rule[1],pe:PackageableElement[1]):Any[*]
{
  if($rule->skipInTests($pe), |[], |$rule.func->eval($pe))
}

function meta::analytics::quality::model::domain::runRules(pkg:PackageableElement[*]) : ViolationInstance[*]
{
     let severity = newMap(^Rankings().severity);

     $pkg->removeDuplicates()
         ->runQualityChecks()
         ->removeDuplicates()
         ->cast(@ViolationInstance)
         ->filter(bug|!$bug.detail.isValid)
         ->filterInstancesToIgnore()
         ->sortBy(b |  $severity->get($b.rule.severity)->toOne());

}

function meta::analytics::quality::model::domain::filterInstancesToIgnore(i:ViolationInstance[*]) : ViolationInstance[*]
{

  $i->filter(v| let ignoreTag = $v.source->match([e:ElementWithTaggedValues[1]| $e->value4Tag('ignore', rule).value,
                                                  fe:FunctionExpression[1]| $fe.usageContext->match(
                                                                            [es:ExpressionSequenceValueSpecificationContext[1]| if($es.functionDefinition->instanceOf(AnnotatedElement),|$es.functionDefinition->cast(@AnnotatedElement)->value4Tag('ignore', rule).value, |[]);,
                                                                             a: ValueSpecificationContext[1]|''
                                                                             ]);,
                                                  a:Any[1]| println($a);'';]);
                if( $ignoreTag->isEmpty()
                ,| true
                ,| let values = $ignoreTag->toOne()->split(',');
                   !$v.rule.id->in($values);
                );

  );
}

function meta::analytics::quality::model::domain::runChecksAsHtml(pkgs:PackageableElement[*],elements:String[*],service:Boolean[1]):String[1]
{
  let bugs = meta::analytics::quality::model::domain::runRules($pkgs);
  let bugDetailsTable = [
        '
', '', '', '', '', '', '', '', '' ]->concatenate( $bugs->map({bug| let pe = $bug.source->findPackagableElement(); let peName = $bug.source->match([p:PackageableElement[1]| $p->fullPackageName('::'),a:Any[1]|$pe->fullPackageName('::') ]) ; [ '', (''), (''), (''), (''), if($service ,|('') ,|('') ), '' ]; }) )->concatenate([ '
SeverityCategoryRuleMessageElement
' + $bug.rule.severity->makeString() + '' + $bug.rule.category->makeString() + '' + $bug.rule.description->makeString() + '' + $bug.detail.message->makeString() + '' + $pe->cast(@PackageableElement)->fullPackageName('::') + '' + 'toOne().source + '\",' + $bug.sourceInfo->toOne().startLine->toString() + ',' + $bug.sourceInfo->toOne().startColumn->toString() + ',false); return false;\'>' + $peName+ ''+ '
', '
' ]); let packagesInfo = [ '
', '

', 'Inspected elements:
' ] ->concatenate($elements->map(e | $e + '
')->sort()->joinStrings('')) ->concatenate( ['

']) ; let rulesInfo = [ '
', '', '', '', '', '', '', '', '' ] ->concatenate(classRules()->map(r | $r->createRuleHTMLRow('Classes',$service))) ->concatenate(propertyRules()->map(r | $r->createRuleHTMLRow('Properties',$service))) ->concatenate(functionRules()->map(r | $r->createRuleHTMLRow('Functions',$service))) ->concatenate(enumerationRules()->map(r | $r->createRuleHTMLRow('Enumerations',$service))) ->concatenate(associationRules()->map(r | $r->createRuleHTMLRow('Associations',$service))) ->concatenate([ '
RuleSeverityCategoryDescriptionRule code
', '
' ]); let stats = '
'+ $bugs->meta::analytics::quality::model::domain::buildStatsTable('Rule')+'
'; let scripts = if($service,| ''+ ''+ '' ,|''); let page = [ '', 'PURE Quality Checks', '', '', $scripts, '', '', '', '
', ''] ->concatenate($stats) ->concatenate($packagesInfo) ->concatenate($bugDetailsTable) ->concatenate($rulesInfo) ->concatenate ([ '', '', '' ]) ->joinStrings('\n'); $page; } function <> meta::analytics::quality::model::domain::createRuleHTMLRow(r:Rule[1],type:String[1],service:Boolean[1]):String[1] { let pe = $r.func; let si= $pe->sourceInformation()->toOne(); ''+$type +''+$r.severity->toString() + '' + $r.category->toString() + '' + $r.description +'' + if($service ,|('' + $pe->cast(@PackageableElement)->fullPackageName('::') + '') ,|('' + 'toString() + ',' + $si.startColumn->toString() + ',false); return false;\'>' + $pe->cast(@PackageableElement)->fullPackageName('::') + ''+ '') ); } function <> meta::analytics::quality::model::domain::buildStatsTable(items : ViolationInstance[*], name : String[1]) : String[1] { let severity = newMap(^Rankings().severity); let pairs = $items->map(e| ^Pair,Integer>(first=$e.rule,second=1)); let keys = $pairs.first->distinct()->cast(@Rule); let stats = $keys->map(k|let p=^Pair(first=$severity->get($k.severity)->toOne(),second=$pairs->filter(p|$p.first == $k).second->sum()); ^Pair,Pair>(first=$k, second = $p);) ->sort({a,b| $a.second.first->compare($b.second.first) + (-1* $a.second.second->compare($b.second.second)) }); // sort by severity then count let rows =$stats ->map({p| let percent = (($p.second.second / $items->size()) * 100); [ '', ('' + $p.first.id->makeString() + ''), ('' + $p.first.severity->makeString() + ''), ('' + $p.second.second->makeString() + ''), ('' + if ($percent > 0.1, | format('%.2f', [$percent]), | 'n/a') + '%'), '' ]; })->cast(@String); let statsTable = [ ''] ->concatenate(['']) ->concatenate([(''), '', '', '', '']) ->concatenate($rows) ->concatenate(['
Total',$items->size()->toString(),'Violation Instances
' + $name + 'SeverityCount%
']); $statsTable->joinStrings(''); } function {service.url='/quality/reports/{element}', service.contentType='text/html'} meta::analytics::quality::model::domain::findBugsReports(element : String[1]) : String[1] { let pe = $element->pathToElement(); [$pe]->meta::analytics::quality::model::domain::runChecksAsHtml($pe->fullPackageName('::'),true); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy