├── .gitignore ├── .mailmap ├── .travis.yml ├── Dockerfile ├── README.md ├── doc ├── imgs │ ├── abs_churn_sample.png │ ├── code_age_sample.png │ ├── coupling_sample.png │ ├── crime_cover.jpg │ └── tree_map_sample.png └── intro.md ├── project.clj ├── src └── code_maat │ ├── analysis │ ├── authors.clj │ ├── churn.clj │ ├── code_age.clj │ ├── commit_messages.clj │ ├── communication.clj │ ├── coupling_algos.clj │ ├── effort.clj │ ├── entities.clj │ ├── logical_coupling.clj │ ├── math.clj │ ├── sum_of_coupling.clj │ ├── summary.clj │ └── workarounds.clj │ ├── app │ ├── app.clj │ ├── grouper.clj │ ├── team_mapper.clj │ └── time_based_grouper.clj │ ├── cmd_line.clj │ ├── dataset │ └── dataset.clj │ ├── output │ ├── csv.clj │ └── filters.clj │ ├── parsers │ ├── git.clj │ ├── git2.clj │ ├── hiccup_based_parser.clj │ ├── mercurial.clj │ ├── perforce.clj │ ├── svn.clj │ ├── tfs.clj │ ├── time_parser.clj │ └── xml.clj │ └── test │ └── data_driven.clj └── test └── code_maat ├── analysis ├── authors_test.clj ├── churn_test.clj ├── code_age_test.clj ├── commit_messages_test.clj ├── communication_test.clj ├── coupling_algos_test.clj ├── effort_test.clj ├── entities_test.clj ├── logical_coupling_test.clj ├── math_test.clj ├── sum_of_coupling_test.clj └── test_data.clj ├── app ├── cmd_line_test.clj ├── day_coupled_entities_git.txt ├── grouper_test.clj ├── team_mapper_test.clj ├── time_based_end_to_end_test.clj └── time_based_grouper_test.clj ├── dataset └── dataset_test.clj ├── end_to_end ├── churn_scenario_test.clj ├── empty.git ├── empty.hg ├── empty.p4 ├── empty.xml ├── git2_live_data_test.clj ├── git2_live_data_test_with_group.clj ├── git_live_data_test.clj ├── mercurial_live_data_test.clj ├── mono_git.log ├── mono_git_team_map.csv ├── perforce_live_data_test.clj ├── regex-and-text-layers-definition.txt ├── regex-layers-definition.txt ├── roslyn_git.log ├── sample_p4.log ├── scenario_tests.clj ├── simple.xml ├── simple_git.txt ├── simple_git2.txt ├── simple_hg.txt ├── simple_p4.txt ├── statsvn.log ├── svn_live_data_test.clj ├── team_level_analyses_test.clj ├── text-layers-definition.txt ├── tfs.log ├── tfs_live_data_test.clj └── tpp_hg.log ├── parsers ├── git2_test.clj ├── git_test.clj ├── mercurial_test.clj ├── perforce_test.clj ├── svn_test.clj ├── tfs_test.clj └── time_parser_test.clj └── tools └── test_tools.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | *.jar 7 | *.class 8 | .lein-deps-sum 9 | .lein-failures 10 | .lein-plugins 11 | .lein* 12 | *~ 13 | mytodo.txt 14 | *.txt# 15 | .#* 16 | *.clj# 17 | *.csv 18 | test_data.txt 19 | .idea/ 20 | *.iml 21 | .nrepl-port 22 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Adam Tornhill Adam Petersen 2 | robertc LogicalChaos 3 | Adam Tornhill Adam Petersen Tornhill 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | 3 | jdk: 4 | - openjdk8 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure:alpine 2 | VOLUME /data 3 | LABEL description="code-maat docker image." 4 | 5 | ARG dest=/usr/src/code-maat 6 | 7 | RUN mkdir -p $dest 8 | WORKDIR $dest 9 | COPY project.clj $dest 10 | RUN lein deps 11 | COPY . $dest 12 | RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar 13 | 14 | ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=85.0", "-jar", "app-standalone.jar"] 15 | CMD [] -------------------------------------------------------------------------------- /doc/imgs/abs_churn_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/doc/imgs/abs_churn_sample.png -------------------------------------------------------------------------------- /doc/imgs/code_age_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/doc/imgs/code_age_sample.png -------------------------------------------------------------------------------- /doc/imgs/coupling_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/doc/imgs/coupling_sample.png -------------------------------------------------------------------------------- /doc/imgs/crime_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/doc/imgs/crime_cover.jpg -------------------------------------------------------------------------------- /doc/imgs/tree_map_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/doc/imgs/tree_map_sample.png -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to code-maat 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013-2023 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (defproject code-maat "1.0.5-SNAPSHOT" 7 | :description "A toolset to mine and analyze version control data" 8 | :url "http://www.adamtornhill.com/code/codemaat.htm" 9 | :license {:name "GNU General Public License v3.0" :url "http://www.gnu.org/licenses/gpl.html"} 10 | :dependencies [[org.clojure/clojure "1.8.0"] 11 | [org.clojure/data.zip "0.1.1"] 12 | [incanter/incanter-core "1.5.7"] 13 | [org.clojure/tools.cli "0.3.1"] 14 | [org.clojure/data.csv "0.1.2"] 15 | [clj-time "0.9.0"] 16 | [org.clojure/math.numeric-tower "0.0.4"] 17 | [org.clojure/math.combinatorics "0.1.1"] 18 | [medley "1.4.0"] 19 | [semantic-csv "0.2.1-alpha1"] 20 | [instaparse "1.4.1"]] 21 | :main code-maat.cmd-line 22 | :aot [code-maat.cmd-line] 23 | :jvm-opts ["-Xmx4g" "-Djava.awt.headless=true" "-Xss512M"]) 24 | -------------------------------------------------------------------------------- /src/code_maat/analysis/authors.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013-2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.authors 7 | (:require [code-maat.dataset.dataset :as ds])) 8 | 9 | ;;; This module contains analysis methods related to the authors of the VCS commits. 10 | ;;; Research shows that these metrics (e.g. number of authors of a module) are 11 | ;;; related to the number of quality problems that module exhibits. 12 | ;;; 13 | ;;; Format: 14 | ;;; All analysis expect an Incanter dataset with the following columns: 15 | ;;; :author :entity :rev 16 | 17 | (defn of-module [m ds] 18 | (ds/-dataset [:author] 19 | (set 20 | (ds/-select-by 21 | :author 22 | (ds/-where {:entity m} ds))))) 23 | 24 | (defn all 25 | "Returns a set with the name of all authors." 26 | [ds] 27 | (distinct (ds/-select-by :author ds))) 28 | 29 | (defn- authors-of-entity 30 | [entity-group] 31 | (->> 32 | entity-group 33 | (ds/-select-by :author) 34 | distinct 35 | count)) 36 | 37 | (defn- revisions-in 38 | [ds] 39 | (ds/-nrows ds)) 40 | 41 | (defn- make-entity-with-author-count 42 | [[entity-group entity-changes]] 43 | (let [entity (:entity entity-group)] 44 | [entity 45 | (authors-of-entity entity-changes) 46 | (revisions-in entity-changes)])) 47 | 48 | (defn by-count 49 | "Groups all entities by there total number of authors. 50 | By default, the entities are sorted in descending order. 51 | You can provide an extra, optional argument specifying 52 | a custom criterion. 53 | Returns a dataset with the columns :entity :n-authors." 54 | ([ds options] 55 | (by-count ds options :desc)) 56 | ([ds _options order-fn] 57 | (->> ds 58 | (ds/-group-by :entity) 59 | (map make-entity-with-author-count) 60 | (ds/-dataset [:entity :n-authors :n-revs]) 61 | (ds/-order-by [:n-authors :n-revs] order-fn)))) 62 | -------------------------------------------------------------------------------- /src/code_maat/analysis/churn.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.churn 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [incanter.core :as incanter] 9 | [code-maat.analysis.math :as math])) 10 | 11 | ;;; This module contains functions for calculating churn metrics. 12 | ;;; Code churn is related to the quality of modules; the higher 13 | ;;; the churn, the more post-release defects. 14 | ;;; Further, inspecting the churn trend lets us spot certain 15 | ;;; organization-oriented patterns. For example, we may spot 16 | ;;; integration bottlenecks as spikes just before the end of 17 | ;;; one iteration. 18 | 19 | (defn- throw-on-missing-data 20 | [ds] 21 | (let [columns (set (incanter/col-names ds))] 22 | (when (or (not (columns :loc-added)) 23 | (not (columns :loc-deleted))) 24 | (throw 25 | (IllegalArgumentException. 26 | (str "churn analysis: the given VCS data doesn't contain modification metrics. " 27 | "Check the code-maat docs for supported VCS and correct log format.")))))) 28 | 29 | (defn- as-int 30 | "Binaries are given as a dash. 31 | Ensure these are replaced by zeros." 32 | [v] 33 | (Integer/parseInt 34 | (if (= "-" v) "0" v))) 35 | 36 | (defn- total-churn 37 | [selector ds] 38 | (reduce + 39 | (map as-int (ds/-select-by selector ds)))) 40 | 41 | (defn- revisions-in 42 | [ds] 43 | (->> ds (ds/-select-by :rev) distinct count)) 44 | 45 | (defn- sum-by-group 46 | "Sums the given dataset by a given group and churn. 47 | The given dataset, grouped-ds, is grouped by the column 48 | given as group. 49 | That means, each entry is a pair of some grouping construct 50 | and the changes related to that construct. The changes are 51 | Incanter datasets themselves so we can keep using 52 | Incanter to extract data for each group." 53 | [group grouped] 54 | (for [[group-entry changes] grouped 55 | :let [grouping (group group-entry) 56 | count (revisions-in changes) 57 | added (total-churn :loc-added changes) 58 | deleted (total-churn :loc-deleted changes)]] 59 | [grouping added deleted count])) 60 | 61 | (defn- sum-by-author-contrib 62 | [grouped] 63 | (for [[entity-entry changes] grouped 64 | :let [entity (:entity entity-entry) 65 | author-group (ds/-group-by :author changes) 66 | author-contrib (sum-by-group :author author-group)]] 67 | [entity author-contrib])) 68 | 69 | (defn- normalize-contrib 70 | "Each entity is associated with its contributing authors. 71 | An author may be included multiple times. We need to 72 | join that info and splice one entity into multiple rows 73 | to make up an Incanter dataset. 74 | Example on input: 75 | [Entity [[ta 20 2] [at 2 0]]] 76 | Should result in: 77 | [Entity ta 20 2] 78 | [Entity at 2 0]" 79 | [[name contribs]] 80 | (map (fn [[a add del]] [name a add del]) contribs)) 81 | 82 | (defn- churn-by 83 | [group ds options] 84 | (throw-on-missing-data ds) 85 | (->> 86 | (ds/-group-by group ds) 87 | (sum-by-group group) 88 | (ds/-dataset [group :added :deleted :commits]))) 89 | 90 | (defn absolutes-trend 91 | "Calculates the absolute code churn measures per date. 92 | Returns an Incanter dataset with the number of lines 93 | added and deleted each day (note that only dates wich 94 | involved commits are considered)." 95 | [commits options] 96 | (->> 97 | (churn-by :date commits options) 98 | (ds/-order-by [:date :added :deleted] :asc))) 99 | 100 | (defn by-author 101 | "Sums the total churn for each contributing author." 102 | [commits options] 103 | (->> 104 | (churn-by :author commits options) 105 | (ds/-order-by [:author :added] :asc))) 106 | 107 | (defn by-entity 108 | "Returns the absolute churn of each entity. 109 | The entities are sorted at churn rate in 110 | descending order based on the lines added 111 | metric. The idea is that entities 112 | with higher churn rate (even absolute) are 113 | more likely to contain post-release defects, where 114 | the number of lines added is a better predictor 115 | than lines deleted." 116 | [ds options] 117 | (->> 118 | (churn-by :entity ds options) 119 | (ds/-order-by :added :desc))) 120 | 121 | (defn as-ownership 122 | "Returns a table specifying the ownership of 123 | each module. Ownership is defined as the 124 | amount of churn contributed by each author 125 | to each entity." 126 | [ds options] 127 | (throw-on-missing-data ds) 128 | (->> 129 | (ds/-group-by :entity ds) 130 | sum-by-author-contrib 131 | (mapcat normalize-contrib) 132 | (ds/-dataset [:entity :author :added :deleted]) 133 | (ds/-order-by :entity :asc))) 134 | 135 | ;; 136 | ;;; Algorithms to identify main developers from churn 137 | ;;; 138 | 139 | (defn- added-lines 140 | [[_author added _deleted]] 141 | added) 142 | 143 | (defn- removed-lines 144 | [[_author _added deleted]] 145 | deleted) 146 | 147 | (def developer first) 148 | 149 | (defn- as-ownership-ratio 150 | [own total] 151 | (->> 152 | (max total 1) ; some entities don't have any added lines (just removed) 153 | (/ own) 154 | math/ratio->centi-float-precision)) 155 | 156 | (defn- pick-main-developer 157 | "Picks the developer that contributed most lines of 158 | code (sure, a rough measure). 159 | Returns [Entity Developer Added Total-Added] 160 | Example on input: 161 | [Entity [[ta 20 2] [at 2 0]]] 162 | Should result in: 163 | [Entity ta 20 2]" 164 | ([author-contrib] 165 | (pick-main-developer added-lines author-contrib)) 166 | ([metric-fn [name contribs]] 167 | (let [total-contrib (reduce + (map metric-fn contribs)) 168 | main-dev (first (reverse (sort-by metric-fn contribs))) 169 | main-dev-contrib (metric-fn main-dev) 170 | ownership-ratio (as-ownership-ratio main-dev-contrib total-contrib)] 171 | [name (developer main-dev) main-dev-contrib total-contrib ownership-ratio]))) 172 | 173 | (defn- grouped-by-author-contrib 174 | [ds] 175 | (throw-on-missing-data ds) 176 | (-> 177 | (ds/-group-by :entity ds) 178 | sum-by-author-contrib)) 179 | 180 | 181 | (defn by-main-developer 182 | "Identifies the main developer of each entity. 183 | The main developer is the one who has contributed 184 | most lines of code (default case). 185 | NOTE: see the alternative algorithm below!" 186 | [ds options] 187 | (->> 188 | (grouped-by-author-contrib ds) 189 | (map pick-main-developer) 190 | (ds/-dataset [:entity :main-dev :added :total-added :ownership]) 191 | (ds/-order-by :entity :asc))) 192 | 193 | (defn by-refactoring-main-developer 194 | "Identifies the main developer of each entity. 195 | The main developer in this alternative calculation 196 | is the developer that has _removed_ most lines. 197 | The idea/speculation is that when you remove code, 198 | you make a more active design choice than what can be 199 | expected from addition alone. We speak refactoring here." 200 | [ds options] 201 | (->> 202 | (grouped-by-author-contrib ds) 203 | (map (partial pick-main-developer removed-lines)) 204 | (ds/-dataset [:entity :main-dev :removed :total-removed :ownership]) 205 | (ds/-order-by :entity :asc))) 206 | -------------------------------------------------------------------------------- /src/code_maat/analysis/code_age.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.code-age 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [code-maat.parsers.time-parser :as tp] 9 | [clj-time.core :as tc])) 10 | 11 | ;;; The following analysis is inspired by Dan North's presentation 12 | ;;; on keeping a short software half-life. 13 | ;;; 14 | ;;; The Code Age analysis is based on the idea that we want 15 | ;;; to have code that's either: 16 | ;;; 1) So old that it's a commodity stored away in stable libraries, or 17 | ;;; 2) Fresh in our minds so that we remember what it does. 18 | ;;; 19 | ;;; The algorithms in this module will calculate the age of each 20 | ;;; entity in months with respect to the last time the code was 21 | ;;; modified. It's then up to us to visualize it in a sensible way. 22 | 23 | (def time-parser (tp/time-parser-from "YYYY-MM-dd")) 24 | 25 | (defn- as-time 26 | [time-as-string] 27 | (time-parser time-as-string)) 28 | 29 | (defn- time-now 30 | [options] 31 | (if-let [given-time (:age-time-now options)] 32 | (as-time given-time) 33 | (tc/now))) 34 | 35 | (defn- changes-within-time-span 36 | [changes now] 37 | (ds/-where {:date {:$fn 38 | (fn [date] (tc/before? (as-time date) now))}} 39 | changes)) 40 | 41 | (defn- latest-modification 42 | [changes] 43 | (-> 44 | (ds/-select-by :date changes) 45 | sort 46 | last)) 47 | 48 | (defn- age-of-latest-in 49 | [changes now] 50 | (-> 51 | (latest-modification changes) 52 | as-time 53 | (tc/interval now) 54 | tc/in-months)) 55 | 56 | (def has-content (complement ds/-empty?)) 57 | 58 | (defn- entities-by-latest-modification 59 | [now grouped] 60 | (for [[entity-entry changes] grouped 61 | :let [entity (:entity entity-entry) 62 | relevant-changes (changes-within-time-span changes now)] 63 | :when (has-content relevant-changes)] 64 | [entity (age-of-latest-in relevant-changes now)])) 65 | 66 | (defn by-age 67 | [ds options] 68 | (->> 69 | (ds/-group-by :entity ds) 70 | (entities-by-latest-modification (time-now options)) 71 | (ds/-dataset [:entity :age-months]) 72 | (ds/-order-by [:age-months] :asc))) 73 | -------------------------------------------------------------------------------- /src/code_maat/analysis/commit_messages.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.commit-messages 7 | (:require [code-maat.dataset.dataset :as dataset] 8 | [incanter.core :as incanter])) 9 | 10 | ;;; This module helps you analyze version-control data 11 | ;;; based on commit messages. 12 | ;;; Our commit messages contain information about our process and 13 | ;;; the kind of work we do. 14 | ;;; For example, we can use that information to extract 15 | ;;; statistics on bug distributions. Note that this data is 16 | ;;; heuristics, not absolute truths (for that you need to mine 17 | ;;; your bug tracking system). 18 | ;;; 19 | ;;; Usage: just provide a regular expression that specifies 20 | ;;; the words of interest in :expression-to-match. 21 | 22 | 23 | (defn- as-word-match-expr 24 | [raw-expr] 25 | (re-pattern raw-expr)) 26 | 27 | (defn- match-expr-from 28 | [options] 29 | (if-let [mexpr (:expression-to-match options)] 30 | (as-word-match-expr mexpr) 31 | (throw 32 | (IllegalArgumentException. 33 | "Commit messages: you need to provide an expression to match against.")))) 34 | 35 | (defn- commit-matches 36 | "Performs a match for the expression provided by the 37 | user against a single commit message." 38 | [mexpr line] 39 | (re-find mexpr line)) 40 | 41 | (defn- rows-matching-given-expr 42 | [mexpr ds] 43 | (dataset/-where {:message {:$fn (fn [m] (commit-matches mexpr m))}} ds)) 44 | 45 | (defn- as-matching-entity-freqs 46 | [ds] 47 | (->> 48 | (dataset/-select-by :entity ds) 49 | incanter/to-vect 50 | frequencies 51 | (into []))) 52 | 53 | (defn- commit-log-without-messages? 54 | [ds] 55 | (let [ncommits-with-message (->> ds (dataset/-where {:message {:$fn (fn [m] (not= m "-"))}}) dataset/-nrows) 56 | nrows (dataset/-nrows ds)] 57 | (and (pos? nrows) 58 | (= 0 ncommits-with-message)))) 59 | 60 | (defn- ensure-supported-vcs 61 | "The git2 format doesn't support this analysis since the commit messages aren't included. Here we make a more 62 | general check." 63 | [ds] 64 | (if (commit-log-without-messages? ds) 65 | (throw (IllegalArgumentException. (str "Wrong version-control format. Cannot do a messages analysis without commit messages. " 66 | "Look at the difference between the git and git2 formats in the docs."))) 67 | ds)) 68 | 69 | (defn by-word-frequency 70 | "Returns the frequencies of the given word matches 71 | across all entities. 72 | This analysis is typically used to extrapolate 73 | bug fixes from the commit messages. 74 | For example, the user may specify a list of 75 | suspicious words like bug, error, etc and 76 | this function counts the occourences." 77 | [ds options] 78 | (->> 79 | ds 80 | ensure-supported-vcs 81 | (rows-matching-given-expr (match-expr-from options)) 82 | as-matching-entity-freqs 83 | (incanter/dataset [:entity :matches]) 84 | (dataset/-order-by [:matches :entity] :desc))) 85 | -------------------------------------------------------------------------------- /src/code_maat/analysis/communication.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.communication 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [code-maat.analysis.effort :as effort] 9 | [clojure.math.combinatorics :as combo] 10 | [code-maat.analysis.math :as m] 11 | [clojure.math.numeric-tower :as math])) 12 | 13 | ;;; This module attempts to give some heuristics on 14 | ;;; the communication needs of a project. 15 | ;;; The idea is basedo on Conway's law - a project 16 | ;;; works best when its organizational structure is 17 | ;;; mirrored in software. 18 | ;;; 19 | ;;; The algorithm is similiar to the one used for 20 | ;;; logical coupling: calculate the number of shared 21 | ;;; commits between all permutations of authors. 22 | ;;; Based on their total averaged commits, a 23 | ;;; communication strength value is calculated. 24 | 25 | (defn- authors-of 26 | [changes] 27 | (distinct 28 | (ds/-select-by :author changes))) 29 | 30 | ;;; When calculating frequencies we get all permutations, 31 | ;;; including "noise" like self-self pairs. We use that 32 | ;;; noise to carry information for us - self-self will 33 | ;;; give us a fast way to look-up the total number of 34 | ;;; commits for an author. 35 | 36 | (defn- authorship-combos 37 | [authors] 38 | (combo/selections authors 2)) 39 | 40 | (defn- entity-group->authorship-combos 41 | [[entity-entry changes]] 42 | (authorship-combos (authors-of changes))) 43 | 44 | (defn- author-pairs-for-entities 45 | "Transforms the given dataset (grouped on entity) into 46 | a seq of author pairs. The frequency of each pair in 47 | the returned seq will specify their amount of shared 48 | work over the grouped entities." 49 | [grouped] 50 | (mapcat entity-group->authorship-combos grouped)) 51 | 52 | (defn- by-shared-work-frequency 53 | [authors-by-work] 54 | (frequencies authors-by-work)) 55 | 56 | (defn- commits-of 57 | [author freqs] 58 | (freqs [author author])) 59 | 60 | (defn- strength-from 61 | [shared-commits average-commits] 62 | (int 63 | (m/as-percentage 64 | (/ shared-commits average-commits)))) 65 | 66 | (defn- with-commit-stats 67 | "The statistics are calculated from the raw 68 | data, freqs. The data contains pairs for all 69 | authors with their shared work frequencies. 70 | The statistics (i.e. total number of commits) for 71 | any author is retrieved by looking-up the 72 | value for the author paired with himself. 73 | That self-pairing is stripped from the final 74 | statistics but used here to carry information." 75 | [freqs] 76 | (for [[pair shared-commits] freqs 77 | :let [[me peer] pair 78 | my-commits (commits-of me freqs) 79 | peer-commits (commits-of peer freqs) 80 | average-commits (math/ceil 81 | (m/average my-commits peer-commits)) 82 | strength (strength-from shared-commits average-commits)] 83 | :when (not (= me peer))] 84 | [me peer shared-commits average-commits strength])) 85 | 86 | (defn by-shared-entities 87 | "Caclulates the communication needs as based upon 88 | shared work by the authors on different entities. 89 | Returns a dataset containing pairs of all permutations 90 | of authors with a (heuristic) communication strength 91 | value for each pair." 92 | [ds options] 93 | (->> 94 | (effort/as-revisions-per-author ds options) 95 | (ds/-group-by :entity) 96 | author-pairs-for-entities 97 | by-shared-work-frequency 98 | with-commit-stats 99 | (ds/-dataset [:author :peer :shared :average :strength]) 100 | (ds/-order-by [:strength :author] :desc))) 101 | -------------------------------------------------------------------------------- /src/code_maat/analysis/coupling_algos.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.coupling-algos 7 | (:require [clojure.math.combinatorics :as combo] 8 | [code-maat.dataset.dataset :as ds] 9 | [clojure.math.numeric-tower :as math] 10 | [incanter.core :as incanter])) 11 | 12 | ;;; This module contains the shared algorithms for the 13 | ;;; different coupling measures. 14 | 15 | (defn- drop-duplicates 16 | [entities] 17 | (remove #(= % (reverse %)) entities)) 18 | 19 | (defn- drop-mirrored-modules 20 | "Removed mirrored change sets such as: 21 | [A B] [B A] => [A B]" 22 | [entities] 23 | (-> 24 | (map sort entities) 25 | distinct)) 26 | 27 | (defn- as-co-changing-modules 28 | "Returns pairs representing the modules 29 | coupled in the given change set. 30 | Note that we keep single modules that 31 | aren't coupled - we need them to calculate 32 | the correct number of total revisions." 33 | [entities] 34 | (-> 35 | (combo/selections entities 2) 36 | drop-mirrored-modules)) 37 | 38 | (defn as-entities-by-revision 39 | "Extracts the change set per revision 40 | from an Incanter dataset." 41 | [ds] 42 | (->> 43 | (incanter/$ [:rev :entity] ds) ; minimal 44 | (ds/-group-by :rev) 45 | (map second))) 46 | 47 | (defn within-threshold? 48 | "Used to filter the results based on user options." 49 | [{:keys [min-revs min-shared-revs min-coupling max-coupling]} 50 | revs shared-revs coupling] 51 | {:pre [(and min-revs min-shared-revs min-coupling max-coupling)]} 52 | (and 53 | (>= revs min-revs) 54 | (>= shared-revs min-shared-revs) 55 | (>= coupling min-coupling) 56 | (<= (math/floor coupling) max-coupling))) 57 | 58 | (def entities-in-rev 59 | (partial ds/-select-by :entity)) 60 | 61 | (def modules-in-one-rev 62 | "We receive pairs of co-changing modules in a 63 | revision and return a seq of all distinct modules." 64 | (comp distinct flatten)) 65 | 66 | (defn module-by-revs 67 | "Returns a map with each module as key and 68 | its number of revisions as value. 69 | This is used when calculating the degree 70 | of coupling later." 71 | [all-co-changing] 72 | (-> 73 | (mapcat modules-in-one-rev all-co-changing) 74 | frequencies)) 75 | 76 | (defn exceeds-max-changeset-size? 77 | [max-size change-set] 78 | (> (count change-set) max-size)) 79 | 80 | (defn co-changing-by-revision 81 | "Calculates a vector of all entities coupled 82 | in the revision represented by the dataset." 83 | [ds options] 84 | (->> 85 | (as-entities-by-revision ds) 86 | (map entities-in-rev) 87 | (remove (partial exceeds-max-changeset-size? (:max-changeset-size options))) 88 | (map as-co-changing-modules))) 89 | 90 | (defn coupling-frequencies 91 | "Returns a map with pairs of coupled 92 | modules (pairs) as keyes and their 93 | number of shared revisions as value." 94 | [co-changing] 95 | (-> 96 | (apply concat co-changing) 97 | drop-duplicates ; remember: included to get the right total revisions 98 | frequencies 99 | vec)) 100 | -------------------------------------------------------------------------------- /src/code_maat/analysis/effort.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.effort 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [clojure.math.numeric-tower :as m] 9 | [code-maat.analysis.math :as math])) 10 | 11 | ;;; The idea behind effort is to identify how much each author 12 | ;;; contributed to a module. The measure here is a bit more 13 | ;;; rough than the churn metrics. On the other hand, the metric 14 | ;;; is available for all supported VCS. 15 | ;;; I use the generated statistics as a guide when refactoring; 16 | ;;; by ranking the authors based on their amount of contribution 17 | ;;; I know who to ask when visiting a new module. 18 | ;;; 19 | ;;; The analysis in the module is based on research by 20 | ;;; Marco D’Ambros, Harald C. Gall, Michele Lanza, and Martin Pinzger. 21 | 22 | (defn normalize-effort 23 | [[name effort]] 24 | (map (fn [[author revs total-revs]] 25 | [name author revs total-revs]) 26 | effort)) 27 | 28 | (defn- entity-name-from 29 | [[name _]] 30 | name) 31 | 32 | (defn- revs-from 33 | [[_name _author revs _]] 34 | revs) 35 | 36 | (defn- sum-revs-by-author 37 | "Sums the given dataset by a given group and churn. 38 | The given dataset, grouped-ds, is grouped by the column 39 | given as group. 40 | That means, each entry is a pair of some grouping construct 41 | and the changes related to that construct. The changes are 42 | Incanter datasets themselves so we can keep using 43 | Incanter to extract data for each group." 44 | [grouped total-revs] 45 | (for [[group-entry changes] grouped 46 | :let [author (:author group-entry) 47 | revs (ds/-nrows changes)]] 48 | [author revs total-revs])) 49 | 50 | (defn- sum-effort-by-author 51 | [grouped] 52 | (for [[entity-entry changes] grouped 53 | :let [entity (:entity entity-entry) 54 | total-revs (ds/-nrows changes) 55 | author-group (ds/-group-by :author changes) 56 | author-revs (sum-revs-by-author author-group total-revs)]] 57 | [entity author-revs])) 58 | 59 | (defn as-revisions-per-author 60 | [ds _options] 61 | (->> 62 | (ds/-group-by :entity ds) 63 | sum-effort-by-author 64 | (mapcat normalize-effort) 65 | ; note: Clojure implements stable sort which is why this works 66 | (sort-by revs-from >) 67 | (sort-by entity-name-from) 68 | (ds/-dataset [:entity :author :author-revs :total-revs]))) 69 | ;(ds/-order-by :entity :asc))) 70 | 71 | (defn- contributed-revs 72 | [author-changes] 73 | (let [[_author added _total] author-changes] 74 | added)) 75 | 76 | (defn- pick-main-dev-by-rev 77 | [entity-ds] 78 | (let [[entity entity-changes] entity-ds 79 | main-dev-changes (first (sort-by contributed-revs > entity-changes)) 80 | [author added total] main-dev-changes 81 | ownership (math/ratio->centi-float-precision (/ added total))] 82 | [entity author added total ownership])) 83 | 84 | (defn as-main-developer-by-revisions 85 | "Identifies the main developers, together with their 86 | ownership percentage, of each module." 87 | [ds _options] 88 | (->> 89 | (ds/-group-by :entity ds) 90 | sum-effort-by-author 91 | (map pick-main-dev-by-rev) 92 | (ds/-dataset [:entity :main-dev :added :total-added :ownership]) 93 | (ds/-order-by :entity :asc))) 94 | 95 | (defn- as-author-fractals 96 | [[_ ai nc]] 97 | (m/expt (/ ai nc) 2)) 98 | 99 | (defn- as-fractal-value 100 | [[name effort]] 101 | (let [[_1 _2 total-revs] (first effort) ; same as nc 102 | fv1 (reduce + (map as-author-fractals effort)) 103 | fv (math/ratio->centi-float-precision (- 1 fv1))] 104 | [name fv total-revs])) 105 | 106 | (defn as-entity-fragmentation 107 | "Caclulates a fractal value for each entity. 108 | The fractal value ranges from 0 (one author) to 109 | 1 (many authors, unreachable value). 110 | The fractal value is a good complement to number of 111 | authors analyses since here we reduce smaller contributions 112 | more and get a chance to find the truly fragmented entities." 113 | [ds options] 114 | (->> 115 | (ds/-group-by :entity ds) 116 | sum-effort-by-author 117 | (map as-fractal-value) 118 | (ds/-dataset [:entity :fractal-value :total-revs]) 119 | (ds/-order-by [:fractal-value :total-revs] :desc))) 120 | -------------------------------------------------------------------------------- /src/code_maat/analysis/entities.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.entities 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [incanter.core :as incanter])) 9 | 10 | (defn all [ds] 11 | (distinct (ds/-select-by :entity ds))) 12 | 13 | (defn- group->entity-with-rev-count 14 | [[entity-group changes]] 15 | [(:entity entity-group) 16 | (count 17 | (ds/-select-by :rev changes))]) 18 | 19 | (defn all-revisions 20 | [ds] 21 | (distinct (ds/-select-by :rev ds))) 22 | 23 | (defn as-dataset-by-revision 24 | [ds] 25 | (->> 26 | ds 27 | (ds/-group-by :entity) 28 | (map group->entity-with-rev-count) 29 | (ds/-dataset [:entity :n-revs]))) 30 | 31 | (defn revisions-of 32 | "Returns the total number of revisions for the given 33 | entity in the dataset ds, which must be grouped 34 | by revisions." 35 | [entity by-revision-ds] 36 | (incanter/$ ; here we actually want to return a single value! 37 | :n-revs 38 | (ds/-where {:entity entity} by-revision-ds))) 39 | 40 | (defn by-revision 41 | "Sorts all entities in the dataset ds by 42 | their number of revisions." 43 | ([ds options] 44 | (by-revision ds options :desc)) 45 | ([ds options order-fn] 46 | (ds/-order-by :n-revs order-fn 47 | (as-dataset-by-revision ds)))) 48 | -------------------------------------------------------------------------------- /src/code_maat/analysis/logical_coupling.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.logical-coupling 7 | (:require [code-maat.analysis.coupling-algos :as c] 8 | [code-maat.dataset.dataset :as ds] 9 | [code-maat.analysis.math :as m] 10 | [clojure.math.numeric-tower :as math] 11 | [incanter.core :as incanter])) 12 | 13 | ;;; This module calculates the logical coupling of all modules. 14 | ;;; 15 | ;;; Logical coupling refers to modules that tend to change together. 16 | ;;; It's information that's recorded in our version-control systems (VCS). 17 | ;;; 18 | ;;; Input: all analysis expect an Incanter dataset with (at least) the following columns: 19 | ;;; :entity :rev 20 | ;;; 21 | ;;; Oputput: the analysis returns an Incanter dataset with the following columns: 22 | ;;; :entity :coupled :degree :average-revs 23 | 24 | (defn- as-logical-coupling-measure 25 | "This is where the result is assembled. 26 | We already have all the data. Now we just pass through the 27 | coupled modules, with their co-change frequencies, and 28 | transform it to a degree of coupling. 29 | The coupling formula is simple: the number of shared 30 | revisions divided by the average number of revisions for 31 | the two coupled modules." 32 | [ds options within-threshold-fn?] 33 | (let [co-changing (c/co-changing-by-revision ds options) 34 | module-revs (c/module-by-revs co-changing) 35 | coupling (c/coupling-frequencies co-changing)] 36 | (for [[[first-entity second-entity] shared-revs] coupling 37 | :let [first-entity-revisions (module-revs first-entity) 38 | second-entity-revisions (module-revs second-entity) 39 | average-revs (m/average first-entity-revisions 40 | second-entity-revisions) 41 | coupling (m/as-percentage (/ shared-revs average-revs))] 42 | :when (within-threshold-fn? average-revs shared-revs coupling)] 43 | {:entity first-entity 44 | :coupled second-entity 45 | :degree (int coupling) 46 | :average-revs (math/ceil average-revs) 47 | ; verbose options: 48 | :first-entity-revisions first-entity-revisions 49 | :second-entity-revisions second-entity-revisions 50 | :shared-revisions shared-revs}))) 51 | 52 | (defn- results-depending-on 53 | [{:keys [verbose-results] :as _options} 54 | results] 55 | (let [coupling-results [:entity :coupled :degree :average-revs] 56 | verbose-details [:first-entity-revisions :second-entity-revisions :shared-revisions]] 57 | (if verbose-results 58 | (ds/-dataset (into coupling-results verbose-details) results) 59 | (ds/-dataset coupling-results results)))) 60 | 61 | (defn by-degree 62 | "Calculates the degree of logical coupling. Returns a seq 63 | sorted in descending order (default) or an optional, custom sorting criterion. 64 | The calulcation is based on the given coupling statistics. 65 | The coupling is calculated as a percentage value based on 66 | the number of shared commits between coupled entities divided 67 | by the average number of total commits for the coupled entities." 68 | ([ds options] 69 | (by-degree ds options :desc)) 70 | ([ds options order-fn] 71 | (->> 72 | (partial c/within-threshold? options) 73 | (as-logical-coupling-measure ds options) 74 | (results-depending-on options) 75 | (incanter/$order [:degree :average-revs] order-fn)))) 76 | -------------------------------------------------------------------------------- /src/code_maat/analysis/math.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013-2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.math) 7 | 8 | (defn average [& vals] 9 | (/ 10 | (reduce + vals) 11 | (count vals))) 12 | 13 | (defn as-percentage [v] 14 | (* v 100)) 15 | 16 | (defn ratio->centi-float-precision 17 | [v] 18 | (double (with-precision 2 (bigdec v)))) 19 | -------------------------------------------------------------------------------- /src/code_maat/analysis/sum_of_coupling.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014-2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.sum-of-coupling 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [code-maat.analysis.coupling-algos :as c] 9 | [incanter.core :as incanter])) 10 | 11 | ;;; This module calculates the sum of the temporal coupling for each module. 12 | ;;; 13 | ;;; The metric gives the number of shared transactions for a module. 14 | ;;; This gives you a priority list of the modules that are most 15 | ;;; frequently changed together with others. 16 | ;;; 17 | ;;; The analysis returns a dataset with the following columns: 18 | ;;; :entity :soc 19 | ;;; where 20 | ;;; :entity => the name of the module 21 | ;;; :soc => the sum of the coupling 22 | 23 | (defn- entities-by-revision 24 | [ds] 25 | (->> 26 | (c/as-entities-by-revision ds) 27 | (map c/entities-in-rev))) 28 | 29 | (defn- counted-entities 30 | [entities-in-rev] 31 | (let [n-couples (- (count entities-in-rev) 1)] 32 | (map (fn [e] [e n-couples]) entities-in-rev))) 33 | 34 | (defn- entities-with-coupling-count-by-rev 35 | [ds] 36 | (->> ds 37 | entities-by-revision 38 | (mapcat counted-entities))) 39 | 40 | (defn as-soc 41 | "Calculates a Sum of Coupling for each entity in 42 | the dataset that passes the threshold for minimum 43 | number of revisions." 44 | [ds {:keys [min-revs]}] 45 | (->> ds 46 | entities-with-coupling-count-by-rev 47 | (reduce (fn [acc [e n]] 48 | (update-in acc [e] (fnil + 0) n)) 49 | {}) 50 | (into []) 51 | (filter (fn [[e n]] 52 | (> n min-revs))))) 53 | 54 | (defn by-degree 55 | "Calculates the sum of coupling. Returns a seq 56 | sorted in descending order (default) or an optional, 57 | custom sorting criterion." 58 | ([ds options] 59 | (by-degree ds options :desc)) 60 | ([ds options order-fn] 61 | (->> 62 | (as-soc ds options) 63 | (ds/-dataset [:entity :soc]) 64 | (incanter/$order [:soc :entity] order-fn)))) 65 | -------------------------------------------------------------------------------- /src/code_maat/analysis/summary.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.summary 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [code-maat.analysis.authors :as authors] 9 | [code-maat.analysis.entities :as entities])) 10 | 11 | ;;; This module implements a summary analysis of a given change set. 12 | ;;; The intent is to provide an overview of the data under analysis. 13 | 14 | (defn calculate-summary 15 | "Calculates a summary for the data we'll analyze. 16 | Note that the results may differ from the ones in 17 | the VCS log since empty change sets (such as merges) are 18 | ignored in the mining." 19 | [ds] 20 | [["number-of-commits" (count (entities/all-revisions ds))] 21 | ["number-of-entities" (count (entities/all ds))] 22 | ["number-of-entities-changed" (ds/-nrows ds)] 23 | ["number-of-authors" (count (authors/all ds))]]) 24 | 25 | (defn overview 26 | [ds & _] 27 | (ds/-dataset [:statistic :value] 28 | (calculate-summary ds))) 29 | -------------------------------------------------------------------------------- /src/code_maat/analysis/workarounds.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.workarounds) 7 | 8 | (defn fix-single-return-value-bug 9 | "Workaround for what seems to be a flaw in Incanter. 10 | When returning a single value, that value is returned, 11 | not a seq." 12 | [r] 13 | (if (seq? r) r [r])) -------------------------------------------------------------------------------- /src/code_maat/app/app.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.app.app 7 | (:require [code-maat.parsers.svn :as svn] 8 | [code-maat.parsers.git :as git] 9 | [code-maat.parsers.git2 :as git2] 10 | [code-maat.parsers.mercurial :as hg] 11 | [code-maat.parsers.perforce :as p4] 12 | [code-maat.parsers.tfs :as tfs] 13 | [code-maat.parsers.xml :as xml] 14 | [incanter.core :as incanter] 15 | [clojure.string :as string] 16 | [code-maat.output.csv :as csv-output] 17 | [code-maat.analysis.authors :as authors] 18 | [code-maat.analysis.entities :as entities] 19 | [code-maat.analysis.logical-coupling :as coupling] 20 | [code-maat.analysis.sum-of-coupling :as soc] 21 | [code-maat.analysis.summary :as summary] 22 | [code-maat.analysis.churn :as churn] 23 | [code-maat.analysis.effort :as effort] 24 | [code-maat.app.grouper :as grouper] 25 | [code-maat.app.time-based-grouper :as time-grouper] 26 | [code-maat.app.team-mapper :as team-mapper] 27 | [code-maat.analysis.communication :as communication] 28 | [code-maat.analysis.commit-messages :as commits] 29 | [code-maat.analysis.code-age :as age])) 30 | 31 | ;;; Principles: 32 | ;;; 33 | ;;; All individual parts (parsers, analyses, outputs) are kept in 34 | ;;; separate, independet units. 35 | ;;; 36 | ;;; This top-level program (app - lousy name) glues the individual 37 | ;;; parts together into a pipeline of behaviour. The parts are 38 | ;;; selected based on the option passed in from the user interface. 39 | ;;; 40 | ;;; The overall flow is: 41 | ;;; 1 Input: raw text-files (log, optional layer spec, etc) 42 | ;;; 2 Parsers: receive the Input, returns a seq of maps. Each map 43 | ;;; describes one modification. 44 | ;;; 3 The output from the parsers is fed into the layer mapping. 45 | ;;; This is an optional step where individual changes may be 46 | ;;; aggregated to fit analyses at architectural boundaries. 47 | ;;; 4 The seq of maps is now transformed into Incanter datasets. 48 | ;;; 5 The analyses receive the datasets. An analysis always returns 49 | ;;; a dataset itself. 50 | ;;; 6 The output stage receives the dataset. 51 | 52 | 53 | ;;; TODO: consider making this dynamic in order to support new 54 | ;;; analysis methods as plug-ins. 55 | (def ^:const supported-analysis 56 | {"authors" authors/by-count 57 | "revisions" entities/by-revision 58 | "coupling" coupling/by-degree 59 | "soc" soc/by-degree 60 | "summary" summary/overview 61 | "identity" (fn [input _] input) ; for debugging - dumps all raw data 62 | "abs-churn" churn/absolutes-trend 63 | "author-churn" churn/by-author 64 | "entity-churn" churn/by-entity 65 | "entity-ownership" churn/as-ownership 66 | "main-dev" churn/by-main-developer 67 | "refactoring-main-dev" churn/by-refactoring-main-developer 68 | "entity-effort" effort/as-revisions-per-author 69 | "main-dev-by-revs" effort/as-main-developer-by-revisions 70 | "fragmentation" effort/as-entity-fragmentation 71 | "communication" communication/by-shared-entities 72 | "messages" commits/by-word-frequency 73 | "age" age/by-age}) 74 | 75 | (defn analysis-names 76 | [] 77 | (->> 78 | (keys supported-analysis) 79 | sort 80 | (string/join ", "))) 81 | 82 | (defn- fail-for-invalid-analysis 83 | [requested-analysis] 84 | (throw (IllegalArgumentException. 85 | (str "Invalid analysis requested: " requested-analysis ". " 86 | "Valid options are: " (analysis-names))))) 87 | 88 | (defn- make-analysis 89 | "Returns the analysis to run while closing over the options. 90 | Each returned analysis method takes a single data set as argument." 91 | [options] 92 | (if-let [analysis (supported-analysis (options :analysis))] 93 | #(analysis % options) 94 | (fail-for-invalid-analysis (options :analysis)))) 95 | 96 | (defn- run-parser-in-error-handling-context 97 | [parse-fn vcs-name] 98 | (try 99 | (parse-fn) 100 | (catch IllegalArgumentException ae 101 | (throw ae)) 102 | (catch Exception e 103 | (throw (IllegalArgumentException. 104 | (str vcs-name ": Failed to parse the given file - is it a valid logfile?")))))) 105 | 106 | (defn- slurp-encoded 107 | [logfile-name options] 108 | (if-let [encoding (:input-encoding options)] 109 | (slurp logfile-name :encoding encoding) 110 | (slurp logfile-name))) 111 | 112 | (defn- hg->modifications 113 | [logfile-name options] 114 | (run-parser-in-error-handling-context 115 | #(hg/parse-log logfile-name options) 116 | "Mercurial")) 117 | 118 | (defn- svn-xml->modifications 119 | [logfile-name options] 120 | (run-parser-in-error-handling-context 121 | #(-> logfile-name xml/file->zip (svn/zip->modification-sets options)) 122 | "svn")) 123 | 124 | (defn- git->modifications 125 | "Legacy parser for git. Maintained for backwards compatibility with 126 | the examples in Your Code as a Crime Scene. Prefer the git2 parser instead." 127 | [logfile-name options] 128 | (run-parser-in-error-handling-context 129 | #(git/parse-log logfile-name options) 130 | "git")) 131 | 132 | (defn- git2->modifications 133 | [logfile-name options] 134 | (run-parser-in-error-handling-context 135 | #(git2/parse-log logfile-name options) 136 | "git2")) 137 | 138 | (defn- p4->modifications 139 | [logfile-name options] 140 | (run-parser-in-error-handling-context 141 | #(p4/parse-log logfile-name options) 142 | "Perforce")) 143 | 144 | (defn- tfs->modifications 145 | [logfile-name options] 146 | (run-parser-in-error-handling-context 147 | #(tfs/parse-log logfile-name options) 148 | "TFS")) 149 | 150 | (defn- parser-from 151 | [{:keys [version-control]}] 152 | (case version-control 153 | "svn" svn-xml->modifications 154 | "git" git->modifications 155 | "git2" git2->modifications 156 | "hg" hg->modifications 157 | "p4" p4->modifications 158 | "tfs" tfs->modifications 159 | (throw (IllegalArgumentException. 160 | (str "Invalid --version-control specified: " version-control 161 | ". Supported options are: svn, git, git2, hg, p4, or tfs."))))) 162 | 163 | (defn- aggregate-on-boundaries 164 | "The individual changes may be aggregated into layers 165 | of architectural significance. This is done by re-mapping 166 | the name. 167 | In case there isn't any specified grouping, just work on 168 | the raw modifications." 169 | [options commits] 170 | (if-let [grouping (:group options)] 171 | (grouper/run grouping commits) 172 | commits)) 173 | 174 | (defn- aggregate-on-temporal-period 175 | "Groups individual commits into aggregates based on 176 | the given temporal period. Allows the user to treat 177 | all commits during one day as a single, logical change set. 178 | NOTE: will probably not work with author's analyses!!!" 179 | [{:keys [temporal-period] :as options} 180 | commits] 181 | (if temporal-period 182 | (time-grouper/by-time-period commits options) 183 | commits)) 184 | 185 | (defn- aggregate-authors-in-teams 186 | "Maps individual authors to teams in order to calculate social 187 | metrics on an organizational level." 188 | [options commits] 189 | (if-let [team-map-file (:team-map-file options)] 190 | (team-mapper/run commits (team-mapper/file->author-team-lookup team-map-file)) 191 | commits)) 192 | 193 | (defn- make-stdout-output [options] 194 | (if-let [n-out-rows (:rows options)] 195 | #(csv-output/write-to :stream % n-out-rows) 196 | #(csv-output/write-to :stream %))) 197 | 198 | (defn- make-output [options] 199 | (if-let [output-file (:outfile options)] 200 | #(csv-output/write-to-file output-file :stream %) 201 | (make-stdout-output options))) 202 | 203 | (defn- throw-internal-error [e] 204 | (throw (IllegalArgumentException. 205 | (str "Internal error - please report it. Details = " 206 | (.getMessage e))))) 207 | 208 | (defn- run-with-recovery-point 209 | [analysis-fn changes output-fn!] 210 | (try 211 | (output-fn! (analysis-fn changes)) 212 | (catch AssertionError e ; typically a pre- or post-condition 213 | (throw-internal-error e)) 214 | (catch IllegalArgumentException e 215 | (throw e)) 216 | (catch Exception e 217 | (throw-internal-error e)))) 218 | 219 | (defn- parse-commits-to-dataset 220 | [vcs-parser logfile-name options] 221 | (->> 222 | (vcs-parser logfile-name options) 223 | (aggregate-on-boundaries options) 224 | (aggregate-on-temporal-period options) 225 | (aggregate-authors-in-teams options) 226 | incanter/to-dataset)) 227 | 228 | (defn run 229 | "Runs the application using the given options. 230 | The options are a map with the following elements: 231 | :module - the VCS to parse 232 | :analysis - the type of analysis to run 233 | :rows - the max number of results to include" 234 | [logfile-name options] 235 | (let [vcs-parser (parser-from options) 236 | commits (parse-commits-to-dataset vcs-parser logfile-name options) 237 | analysis (make-analysis options) 238 | output! (make-output options)] 239 | (run-with-recovery-point analysis commits output!))) 240 | -------------------------------------------------------------------------------- /src/code_maat/app/grouper.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | 4 | (ns code-maat.app.grouper 5 | (:require [instaparse.core :as insta]) 6 | (:require [clojure.string :as string])) 7 | 8 | ;;; Code Maat supports analysis according to pre-defined architectual groups. 9 | ;;; These groups are typically architectural boundaries. All data 10 | ;;; will be aggregated into that view before analysis. 11 | 12 | ;; Parsing the group specification 13 | ;; =============================== 14 | 15 | (def ^:const group-grammar 16 | "Here's the instaparse grammar for a regex-based grouping/layering. 17 | We expect the groups to be specified as text: 18 | some-path => some_name 19 | or with a regex: 20 | ^some-regexp$ => some_name 21 | That is, everything matching some-path or some-regexp will be grouped 22 | under some_name." 23 | " 24 | = groups 25 | = (group )* | group 26 | group = path name 27 | path = #'((^[\\w/\\\\\\.\\-]+)|(\\^.+\\$))' 28 | separator = '=>' 29 | name = #'[^\\n]+' 30 | ws = #'\\s' 31 | nl = '\\n'") 32 | 33 | (defn- raise-parse-failure 34 | [f] 35 | (let [reason (with-out-str (print f))] 36 | (throw (IllegalArgumentException. 37 | (str "Invalid group specification: " reason))))) 38 | 39 | (defn- as-grammar-map 40 | "The actual invokation of the parser. 41 | Returns a Hiccup parse tree upon success, 42 | otherwise an informative exception is thrown." 43 | [parser input] 44 | (let [result (insta/parse parser input)] 45 | (if (insta/failure? result) 46 | (raise-parse-failure (insta/get-failure result)) 47 | result))) 48 | 49 | ;;; The parse result from instaparse is given as hiccup vectors. 50 | ;;; We define a set of accessors encapsulating the access to 51 | ;;; the individual parts of the associative vectors. 52 | 53 | (defn- as-path 54 | [v] 55 | (get-in v [1 1])) 56 | 57 | (defn- as-name 58 | [v] 59 | (get-in v [2 1])) 60 | 61 | (defn- as-group-specification 62 | [v] 63 | (let [p (as-path v)] 64 | {:path (if (string/starts-with? p "^") 65 | (re-pattern (str p)) 66 | (re-pattern (str "^" p "/"))) 67 | :name (as-name v)})) 68 | 69 | (defn text->group-specification 70 | "Transforms the given text or regular expression into a 71 | seq of maps specifying the grouping. 72 | Each map will have a path key corresponding to a regex pattern 73 | and a name key corresponding to the grouping logical name: 74 | {:path #'^some/path/' :name 'some_name'} 75 | or 76 | {:path #'^some-regexp$' :name 'some_name'}" 77 | [input] 78 | (let [parser (insta/parser group-grammar)] 79 | (->> 80 | input 81 | (as-grammar-map parser) 82 | (map as-group-specification)))) 83 | 84 | ;; Mapping physical entities to logical groups 85 | ;; =========================================== 86 | 87 | (defn- entity->logical-name 88 | [entity group-exprs] 89 | (some (fn [{:keys [path-match logical-name]}] 90 | (when (re-find path-match entity) 91 | logical-name)) 92 | group-exprs)) 93 | 94 | (defn- commit->commit-by-group 95 | [commit group-exprs] 96 | (update-in commit [:entity] #(entity->logical-name % group-exprs))) 97 | 98 | (defn- within-group? 99 | [group-exprs entity] 100 | (->> 101 | (map :path-match group-exprs) 102 | (some #(re-find % entity)))) 103 | 104 | (defn- as-group-expr 105 | [{:keys [path name]}] 106 | {:path-match path :logical-name name}) 107 | 108 | (defn- as-group-exprs 109 | [group-spec] 110 | (map as-group-expr group-spec)) 111 | 112 | (defn map-entities->groups 113 | "Maps each entity in the commits to one of the pre-defined 114 | architectural boundaries (groups). 115 | The groups are given as a seq of maps. Each map denotes the 116 | physical path to the group together with its logical name. 117 | We translate that path into a regex that matches 118 | entity names with logical names." 119 | [commits groups] 120 | (let [group-exprs (as-group-exprs groups)] 121 | (->> 122 | (filter #(within-group? group-exprs (:entity %)) commits) 123 | (map #(commit->commit-by-group % group-exprs))))) 124 | 125 | (defn- groups-from 126 | [group-info-file] 127 | (slurp group-info-file)) 128 | 129 | (defn run 130 | "This entry point parses the given group info. 131 | All entities in each commit are then re-mapped to one 132 | of the given groups." 133 | [group-info-file commits] 134 | (->> 135 | (groups-from group-info-file) 136 | text->group-specification 137 | (map-entities->groups commits))) 138 | -------------------------------------------------------------------------------- /src/code_maat/app/team_mapper.clj: -------------------------------------------------------------------------------- 1 | (ns code-maat.app.team-mapper 2 | (:require [semantic-csv.core :as sc])) 3 | 4 | (defn file->author-team-lookup 5 | "Parses a given team mapping expect to be a CSV with the columns author,team" 6 | [f] 7 | (->> f 8 | sc/slurp-csv 9 | (map (juxt :author :team)) 10 | (into {}))) 11 | 12 | (defn- author->team 13 | [team-lookup {:keys [author] :as commit}] 14 | (assoc commit :author (get team-lookup author author))) 15 | 16 | (defn run 17 | "Maps individual authors to teams as defined by team-lookup, 18 | which is expected to be a map from author to team (strings). 19 | Any author that isn't included in that mapping is 20 | simply kept as-is (see them as a team in themselves). 21 | This has the advantage that any omissions in the mapping are 22 | detected fast." 23 | [commits team-lookup] 24 | (map (partial author->team team-lookup) commits)) 25 | 26 | -------------------------------------------------------------------------------- /src/code_maat/app/time_based_grouper.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | 4 | (ns code-maat.app.time-based-grouper 5 | (:require [clj-time.core :as t] 6 | [clj-time.format :as tf] 7 | [clj-time.periodic :as time-period] 8 | [clj-time.core :as tc] 9 | [medley.core :as m])) 10 | 11 | ;;; Sometimes we'd like to use a different temporal window than 12 | ;;; the commit. For example, when multiple teams are involved 13 | ;;; changes may have to be done in multiple commits as well. 14 | ;;; Further, some organizations use a workflow that involves 15 | ;;; many small commits. 16 | ;;; To remove these biases we use this module to re-group all 17 | ;;; changes according to a given time window before analysis. 18 | ;;; 19 | ;;; Grouping commits by time involves a sliding window over the 20 | ;;; original commits. This means that logically, the same physical commit 21 | ;;; can be counted multiple times since it overlaps with several slides 22 | ;;; of the window. This works well for change coupling but not hotspots. 23 | ;;; Hence, the validation ensures it's a supported analysis before 24 | ;;; applying the filter. 25 | 26 | (defn- string->date 27 | [s] 28 | (tf/parse (tf/formatters :year-month-day) s)) 29 | 30 | (defn date->string 31 | [d] 32 | (tf/unparse (tf/formatters :year-month-day) d)) 33 | 34 | (defn- date-of 35 | [cs] 36 | (some-> cs first :date string->date)) 37 | 38 | (defn- daily-dates-between 39 | "Create a range of DateTime objects where each date represens one day." 40 | [start end] 41 | (let [feeding-range (time-period/periodic-seq start (tc/days 1)) 42 | end-condition-date (tc/plus end (tc/days 1)) 43 | full-range? (fn [current-date] (t/before? current-date end-condition-date))] 44 | (take-while full-range? feeding-range))) 45 | 46 | (defn- pad-commits-to-complete-time-series 47 | "There are probably many days which don't have any commits. 48 | This functions pads up those days with empty commit sets. That way, we can 49 | partition over the sequence and easily create the sliding window commit set." 50 | [commits] 51 | (let [commits-ascending (sort-by :date commits) 52 | first-commit-date (date-of commits-ascending) 53 | last-commit-date (date-of (reverse commits-ascending)) 54 | commits-on-non-active-days []] 55 | (reduce (fn [acc date-in-range] 56 | (let [as-date (date->string date-in-range) 57 | commits-on-day (get acc as-date commits-on-non-active-days)] 58 | (assoc acc as-date commits-on-day))) 59 | (group-by :date commits) 60 | (daily-dates-between first-commit-date last-commit-date)))) 61 | 62 | (defn- drop-date-key 63 | "We used group-by to get commits by date. Now, drop the key so that 64 | only the commits remain." 65 | [grouped-commits] 66 | (map second grouped-commits)) 67 | 68 | (defn- remove-empty-windows 69 | "Not all dates have commit activity." 70 | [commits-within-sliding-windows] 71 | (remove (fn [cs] 72 | (every? empty? cs)) 73 | commits-within-sliding-windows)) 74 | 75 | (defn- adjust-revision-to 76 | "The edge case is that the same file should only be included once, so 77 | let's filter out duplicates." 78 | [new-rev cs] 79 | (->> cs 80 | (map (fn [c] 81 | (assoc c :rev new-rev))) 82 | (m/distinct-by :entity))) 83 | 84 | (defn- combine-commits-to-logical-changesets 85 | [commits-within-sliding-windows] 86 | (mapcat (fn [commits-in-window] 87 | (let [cs (reduce (partial into) commits-in-window) 88 | latest-day (->> cs (sort-by :date) reverse first :date)] 89 | (adjust-revision-to latest-day cs))) 90 | commits-within-sliding-windows)) 91 | 92 | (defn- combine-sliding-commits 93 | "After partitioning commits according to the sliding window, we 94 | need to deliver a flat sequence where each commit group in the window 95 | represents a logical commitset." 96 | [commits-within-sliding-windows] 97 | (->> commits-within-sliding-windows 98 | remove-empty-windows 99 | combine-commits-to-logical-changesets)) 100 | 101 | (defn- partition-commits-into-sliding-periods-of 102 | [time-period padded-cs] 103 | (->> padded-cs 104 | (sort-by first) 105 | drop-date-key 106 | (partition time-period 1))) 107 | 108 | (defn- commits->sliding-window-seq 109 | [time-period cs] 110 | (->> cs 111 | pad-commits-to-complete-time-series 112 | (partition-commits-into-sliding-periods-of time-period) 113 | combine-sliding-commits)) 114 | 115 | (defn- validated-time-period-from 116 | [{:keys [temporal-period] :as _options}] 117 | (if (re-matches #"\d+" temporal-period) 118 | (int (Double/parseDouble temporal-period)) 119 | (throw (IllegalArgumentException. 120 | (str "Invalid time-period: the given value '" temporal-period "' is not an integer."))))) 121 | 122 | (defn by-time-period 123 | "Alright, this is a hack: we just set the commit ID to 124 | the current date. That makes the rest of the analyses treat 125 | our faked grouping as beloning to the same change set." 126 | [cs options] 127 | (let [time-period (validated-time-period-from options)] 128 | (if (seq cs) 129 | (commits->sliding-window-seq time-period cs) 130 | cs))) 131 | 132 | -------------------------------------------------------------------------------- /src/code_maat/cmd_line.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013-2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.cmd-line 7 | (:gen-class) 8 | (:require [code-maat.app.app :as app] 9 | [clojure.string :as string] 10 | [clojure.tools.cli :as cli])) 11 | 12 | (def cli-options 13 | [["-l" "--log LOG" "Log file with input data"] 14 | ["-c" "--version-control VCS" "Input vcs module type: supports svn, git, git2, hg, p4, or tfs"] 15 | ["-a" "--analysis ANALYSIS" 16 | (str "The analysis to run (" (app/analysis-names) ")") 17 | :default "authors"] 18 | [nil "--input-encoding INPUT-ENCODING" "Specify an encoding other than UTF-8 for the log file"] 19 | ["-r" "--rows ROWS" "Max rows in output" :parse-fn #(Integer/parseInt %)] 20 | ["-o" "--outfile OUTFILE" "Write the result to the given file name"] 21 | ["-g" "--group GROUP" "A file with a pre-defined set of layers. The data will be aggregated according to the group of layers."] 22 | ["-p" "--team-map-file TEAM-MAP-FILE" "A CSV file with author,team that translates individuals into teams."] 23 | ["-n" "--min-revs MIN-REVS" "Minimum number of revisions to include an entity in the analysis" 24 | :default 5 :parse-fn #(Integer/parseInt %)] 25 | ["-m" "--min-shared-revs MIN-SHARED-REVS" "Minimum number of shared revisions to include an entity in the analysis" 26 | :default 5 :parse-fn #(Integer/parseInt %)] 27 | ["-i" "--min-coupling MIN-COUPLING" "Minimum degree of coupling (in percentage) to consider" 28 | :default 30 :parse-fn #(Integer/parseInt %)] 29 | ["-x" "--max-coupling MAX-COUPLING" "Maximum degree of coupling (in percentage) to consider" 30 | :default 100 :parse-fn #(Integer/parseInt %)] 31 | ["-s" "--max-changeset-size MAX-CHANGESET-SIZE" 32 | "Maximum number of modules in a change set if it shall be included in a coupling analysis" 33 | :default 30 :parse-fn #(Integer/parseInt %)] 34 | ["-e" "--expression-to-match MATCH-EXPRESSION" "A regex to match against commit messages. Used with -messages analyses"] 35 | ["-t" "--temporal-period TEMPORAL-PERIOD" 36 | "Used for coupling analyses. Instructs Code Maat to consider all commits during the rolling temporal period as a single, logical commit set"] 37 | ["-d" "--age-time-now AGE-TIME_NOW" "Specify a date as YYYY-MM-dd that counts as time zero when doing a code age analysis"] 38 | [nil "--verbose-results" "Includes additional analysis details together with the results. Only implemented for change coupling."] 39 | ["-h" "--help"]]) 40 | 41 | (defn- usage [options-summary] 42 | (->> ["This is Code Maat, a program used to collect statistics from a VCS." 43 | "Version: 1.0.5-SNAPSHOT" 44 | "" 45 | "Usage: program-name -l log-file [options]" 46 | "" 47 | "Options:" 48 | options-summary 49 | "Please refer to the manual page for more information."] 50 | (string/join \newline))) 51 | 52 | (defn- error-msg [errors] 53 | (str "The following errors occurred while parsing your command:\n\n" 54 | (string/join \newline errors))) 55 | 56 | (defn- exit [status msg] 57 | (println msg) 58 | (System/exit status)) 59 | 60 | (defn -main 61 | [& args] 62 | (let [{:keys [options arguments errors summary]} (cli/parse-opts args cli-options)] 63 | (cond 64 | (:help options) (exit 0 (usage summary)) 65 | errors (exit 1 (error-msg errors))) 66 | :else 67 | (try 68 | (app/run (:log options) options) 69 | (flush) 70 | (catch IllegalArgumentException e 71 | (println "Invalid argument: " (.getMessage e)) 72 | (exit 1 (usage summary))) 73 | (catch Exception e ; this is our main recovery point 74 | (.printStackTrace e) 75 | (println "Error: " (.getMessage e)) 76 | (exit 1 (usage summary)))))) 77 | -------------------------------------------------------------------------------- /src/code_maat/dataset/dataset.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.dataset.dataset 7 | (:require [incanter.core :as incanter] 8 | [code-maat.analysis.workarounds :as workarounds])) 9 | 10 | ;;; This module contains a thin layer around a subset of Incanter core. 11 | ;;; The reason is that Incanter is inconsistent in its return values: 12 | ;;; - for multiple hits, a seq is returned, 13 | ;;; - for a single hit, the sole value is returned. 14 | ;;; This behavior introduces a special case that we want to hide 15 | ;;; from our application level code. 16 | ;;; 17 | ;;; Thus, the responsibility of this module is to provide a uniform API by 18 | ;;; always returning a seq. 19 | 20 | (defmacro def-ds 21 | "Defines a constant Incanter dataset based on 22 | the given vector data. 23 | This macro is typically used in the test cases." 24 | [name data] 25 | (let [ds-name (symbol (str name))] 26 | `(def ^:const ~ds-name 27 | (incanter/to-dataset ~data)))) 28 | 29 | (defn -empty? 30 | [ds] 31 | (= 0 (incanter/nrow ds))) 32 | 33 | (defn -group-by 34 | [group-criterion ds] 35 | (if (-empty? ds) 36 | [] 37 | (incanter/$group-by group-criterion ds))) 38 | 39 | (defn -select-by 40 | [criterion ds] 41 | (workarounds/fix-single-return-value-bug 42 | (incanter/$ criterion ds))) 43 | 44 | (defn -where 45 | [criterion ds] 46 | (incanter/$where criterion ds)) ; TODO: single value? 47 | 48 | (defn -order-by 49 | [criterion order-fn ds] 50 | (incanter/$order criterion order-fn ds)) 51 | 52 | (defn -dataset 53 | [columns data] 54 | (incanter/dataset columns data)) 55 | 56 | (defn -nrows 57 | [ds] 58 | (incanter/nrow ds)) 59 | -------------------------------------------------------------------------------- /src/code_maat/output/csv.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.output.csv 7 | (:require [code-maat.output.filters :as filters] 8 | [clojure.data.csv :as csv] 9 | [clojure.java.io :as io] 10 | [incanter.core :as incanter])) 11 | 12 | ;;; An output module presenting its given Incanter dataset on CSV format. 13 | 14 | (defn write-to 15 | "Writes the given dataset ds as CSV to the given stream s. 16 | By default, all rows are written. This behavior 17 | is possible to override by providing a third argument 18 | specifying the number of rows to write." 19 | ([s ds] 20 | (csv/write-csv *out* [(map name (incanter/col-names ds))]) 21 | (csv/write-csv *out* (incanter/to-list ds))) 22 | ([s ds n-rows] 23 | (write-to s (filters/n-rows ds n-rows)))) 24 | 25 | (defn write-to-file 26 | [file-name s ds] 27 | (with-open [out-file (io/writer file-name)] 28 | (binding [*out* out-file] 29 | (write-to s ds)))) 30 | -------------------------------------------------------------------------------- /src/code_maat/output/filters.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.output.filters 7 | (:require [incanter.core :as incanter])) 8 | 9 | (defn n-rows [ds n] 10 | (let [safe-n (min n (incanter/nrow ds))] 11 | (incanter/sel ds :rows (range safe-n)))) -------------------------------------------------------------------------------- /src/code_maat/parsers/git.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.git 7 | (:require [code-maat.parsers.time-parser :as tp] 8 | [code-maat.parsers.hiccup-based-parser :as hbp])) 9 | 10 | ;;; This module is responsible for parsing a git log file. 11 | ;;; 12 | ;;; Input: a log file generated with the following command: 13 | ;;; 14 | ;;; git log --pretty=format:'[%h] %an %ad %s' --date=short --numstat 15 | ;;; 16 | ;;; Ouput: A sequence of maps where each map represents a change entry 17 | ;;; from the version-control log: 18 | ;;; :entity :date :author :rev 19 | ;;; where 20 | ;;; :entity -> the changed entity as a string 21 | ;;; :date -> commit date as a string 22 | ;;; :author -> as a string 23 | ;;; :rev -> the hash used by git to identify the commit 24 | 25 | (def ^:const git-grammar 26 | "Here's the instaparse grammar for a git entry. 27 | Note that we parse the entries one by one (Instaparse memory optimization). 28 | 29 | In the current version we only extract basic info on 30 | authors and file modification patterns. 31 | To calculate churn, we parse the lines added/deleted too. 32 | That info is added b the numstat argument." 33 | " 34 | entry = prelude changes (* covers pull requests *) 35 | = rev author date message 36 | rev = <'['> #'[\\da-f]+' <']'> 37 | author = #'.+?(?=(\\s\\d{4}-\\d{2}-\\d{2}))' (* match until the date field *) 38 | date = #'\\d{4}-\\d{2}-\\d{2}' 39 | message = #'[^\\n]*' 40 | changes = change* 41 | change = added deleted file 42 | added = numstat 43 | deleted = numstat 44 | = #'[\\d-]*' (* binary files are presented with a dash *) 45 | file = #'.+' 46 | ws = #'\\s' 47 | tab = #'\\t' 48 | nl = '\\n'") 49 | 50 | (def as-common-time-format (tp/time-string-converter-from "YYYY-MM-dd")) 51 | 52 | (def positional-extractors 53 | "Specify a set of functions to extract the parsed values." 54 | {:rev #(get-in % [1 1]) 55 | :author #(get-in % [2 1]) 56 | :date #(as-common-time-format (get-in % [3 1])) 57 | :message #(get-in % [4 1]) 58 | :changes #(rest (get-in % [5]))}) 59 | 60 | (defn parse-log 61 | "Transforms the given input git log into an 62 | Incanter dataset suitable for the analysis modules." 63 | [input-file-name options] 64 | (hbp/parse-log input-file-name 65 | options 66 | git-grammar 67 | positional-extractors)) 68 | 69 | (defn parse-read-log 70 | [input-text options] 71 | (hbp/parse-read-log input-text 72 | options 73 | git-grammar 74 | positional-extractors)) 75 | -------------------------------------------------------------------------------- /src/code_maat/parsers/git2.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.git2 7 | (:require [instaparse.core :as insta] 8 | [code-maat.parsers.time-parser :as tp] 9 | [code-maat.parsers.hiccup-based-parser :as hbp])) 10 | 11 | ;;; This module is responsible for parsing a git log file. 12 | ;;; 13 | ;;; NOTE: this is the prefered git parser - we have one more that 14 | ;;; exists for backwards compatibility, but the git2 parser is 15 | ;;; more tolerant and faster. 16 | ;;; 17 | ;;; Input: a log file generated with the following command: 18 | ;;; 19 | ;;; git log --all -M -C --numstat --date=short --pretty=format:'--%h--%cd--%cn' 20 | ;;; 21 | ;;; Ouput: A sequence of maps where each map represents a change entry 22 | ;;; from the version-control log: 23 | ;;; :entity :date :author :rev 24 | ;;; where 25 | ;;; :entity -> the changed entity as a string 26 | ;;; :date -> commit date as a string 27 | ;;; :author -> as a string 28 | ;;; :rev -> the hash used by git to identify the commit 29 | 30 | ;;; Sample input where each commit is separated by a whitespace: 31 | ;;; 32 | ;;; --586b4eb--2015-06-15--Adam Tornhill 33 | ;;: 35 0 src/code_maat/mining/vcs.clj 34 | 35 | (def ^:const git-grammar 36 | "Here's the instaparse grammar for a git entry. 37 | Note that we parse the entries one by one (Instaparse memory optimization). 38 | 39 | In the current version we only extract basic info on 40 | authors and file modification patterns. 41 | To calculate churn, we parse the lines added/deleted too. 42 | That info is added b the numstat argument." 43 | " 44 | entry = prelude changes (* covers pull requests *) 45 | = rev date author 46 | rev = #'[\\da-f]+' 47 | author = #'[^\\n]*' 48 | date = #'\\d{4}-\\d{2}-\\d{2}' 49 | changes = change* 50 | change = added deleted file 51 | added = numstat 52 | deleted = numstat 53 | = #'[\\d-]*' (* binary files are presented with a dash *) 54 | file = #'.+' 55 | separator = '--' 56 | ws = #'\\s' 57 | tab = #'\\t' 58 | nl = '\\n'") 59 | 60 | (def as-common-time-format (tp/time-string-converter-from "YYYY-MM-dd")) 61 | 62 | (def positional-extractors 63 | "Specify a set of functions to extract the parsed values." 64 | {:rev #(get-in % [1 1]) 65 | :date #(as-common-time-format (get-in % [2 1])) 66 | :author #(get-in % [3 1]) 67 | :changes #(rest (get-in % [4])) 68 | :message (fn [_] "-")}) ; NOTE: use the git legacy parser to extract commit messages 69 | 70 | (defn parse-log 71 | "Transforms the given input git log into an 72 | Incanter dataset suitable for the analysis modules." 73 | [input-file-name options] 74 | (hbp/parse-log input-file-name 75 | options 76 | git-grammar 77 | positional-extractors)) 78 | 79 | (defn parse-read-log 80 | [input-text options] 81 | (hbp/parse-read-log input-text 82 | options 83 | git-grammar 84 | positional-extractors)) 85 | -------------------------------------------------------------------------------- /src/code_maat/parsers/hiccup_based_parser.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013-2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.hiccup-based-parser 7 | (:import [java.io BufferedReader StringReader]) 8 | (:require [instaparse.core :as insta] 9 | [clojure.core.reducers :as r] 10 | [clojure.java.io :as io] 11 | [clojure.string :as s])) 12 | 13 | ;;; This module encapsulates the common functionality of parsing a 14 | ;;; VCS log into Hiccup format using Instaparse. 15 | ;;; Clients parameterize this module with the actual grammar (e.g. git or hg). 16 | 17 | (defn- raise-parse-failure 18 | [f input] 19 | (let [reason (with-out-str (print f))] 20 | (throw (IllegalArgumentException. 21 | (str "input: " input ", reason: " reason))))) 22 | 23 | (defn parse-with 24 | "The actual invokation of the parser. 25 | Returns a Hiccup parse tree upon success, 26 | otherwise an informative exception is thrown." 27 | [parser input] 28 | (let [result (insta/parse parser input)] 29 | (if (insta/failure? result) 30 | (raise-parse-failure (insta/get-failure result) input) 31 | result))) 32 | ;; 33 | ;; Instaparse is great but consumes a lot of memory. 34 | ;; Thus, we cannot parse larger log files in a single pass. 35 | ;; Instead we need to tokenize the input stream into chunks 36 | ;; of entries that can be parsed one by one. 37 | ;; 38 | 39 | (defn- parse-entry 40 | [entry-token parse-fn] 41 | (parse-fn entry-token)) 42 | 43 | (defn- parse-entry-from 44 | [line-as-seq parse-fn] 45 | (-> 46 | (s/join "\n" line-as-seq) 47 | (str "\n") 48 | (parse-entry parse-fn))) 49 | 50 | (defn as-entry-tokens 51 | [] 52 | (fn [rf] 53 | (let [acc (volatile! [])] 54 | (fn 55 | ([] (rf)) 56 | ([result] 57 | (if-let [remaining (seq @acc)] 58 | (rf result remaining) 59 | (rf result))) 60 | ([result input] 61 | (if (s/blank? input) 62 | (let [remaining @acc] 63 | (vreset! acc []) 64 | (rf result remaining)) 65 | (do 66 | (vswap! acc conj input) 67 | result))))))) 68 | 69 | ;; 70 | ;; Transform the Instaparse Hiccup vectors to our own representation (maps) 71 | ;; 72 | 73 | ;;; The parse result from instaparse is given as hiccup vectors. 74 | ;;; We define a set of accessors encapsulating the access to 75 | ;;; the individual parts of the associative vectors. 76 | ;;; Example input: a seq of => 77 | ;;; [:entry 78 | ;;; [:rev "123"] 79 | ;;; [:author "a"] 80 | ;;; [:date "2013-01-30"] 81 | ;;; [:changes 82 | ;;; [:file ...]]] 83 | 84 | (defn- churn-stats? [c] 85 | (= :change (get-in c [0]))) 86 | 87 | (defn- file-stats 88 | "The file statistics are at least the name of the file. 89 | Some VCS (git) allows me to easily include churn stats. 90 | If they're available, I parse them too. 91 | Example: 92 | [[:change [:added 10] [:deleted 9] [:file src/code_maat/parsers/git.clj]]" 93 | [change] 94 | (if (churn-stats? change) 95 | {:name (get-in change [3 1]) 96 | :added (get-in change [1 1]) 97 | :deleted (get-in change [2 1])} 98 | {:name (get-in change [1])})) 99 | 100 | (defn- files [{:keys [changes]} z] 101 | (map file-stats (changes z))) 102 | 103 | (defn- make-row-constructor 104 | [{:keys [author rev date message]} v] 105 | (let [author-value (author v) 106 | rev-value (rev v) 107 | date-value (date v) 108 | message-value (message v)] 109 | (fn [{:keys [name added deleted]}] 110 | (let [mandatory {:author author-value 111 | :rev rev-value 112 | :date date-value 113 | :entity name 114 | :message message-value} 115 | optional {:loc-added added 116 | :loc-deleted deleted}] 117 | (if (and added deleted) 118 | (merge mandatory optional) 119 | mandatory))))) 120 | 121 | (defn- entry-as-row 122 | "Transforms one entry (as a hiccup formated vector) into 123 | a map representing one row in the change information." 124 | [field-extractors v] 125 | (let [row-ctor (make-row-constructor field-extractors v) 126 | files (files field-extractors v)] 127 | (map row-ctor files))) 128 | 129 | ;;; TODO: clean-up: most of the pipeline can be built 130 | ;;; as a combination of the different functions (including 131 | ;;; the second pass through as we transform from hiccup). 132 | 133 | (defn- parse-from 134 | "Expected to be invoked in a with-open context." 135 | [rdr grammar field-extractors] 136 | (let [specific-parser (insta/parser grammar) 137 | parse-fn (partial parse-with specific-parser)] 138 | (->> 139 | (line-seq rdr) 140 | (into [] (as-entry-tokens)) 141 | (r/fold 32 142 | (fn 143 | ([] []) 144 | ([a b] (r/cat a b))) 145 | (fn [acc entry] 146 | (conj acc (parse-entry-from entry parse-fn)))) 147 | (mapcat (partial entry-as-row field-extractors))))) 148 | 149 | (defn- encoding-from 150 | [options] 151 | (get options :input-encoding "UTF-8")) 152 | 153 | (defn parse-log 154 | "Transforms the given input git log into a 155 | seq of maps suitable for the analysis modules." 156 | [input-file-name options grammar field-extractors] 157 | (with-open [rdr (io/reader 158 | input-file-name 159 | :encoding (encoding-from options))] 160 | (parse-from rdr grammar field-extractors))) 161 | 162 | (defn parse-read-log 163 | [log-text options grammar field-extractors] 164 | (with-open [rdr (BufferedReader. (StringReader. log-text))] 165 | (parse-from rdr grammar field-extractors))) 166 | -------------------------------------------------------------------------------- /src/code_maat/parsers/mercurial.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.mercurial 7 | (:require [code-maat.parsers.hiccup-based-parser :as hbp] 8 | [code-maat.parsers.time-parser :as tp])) 9 | 10 | ;;; This module is responsible for parsing a Mercurial log file. 11 | ;;; 12 | ;;; Input: a log file generated with the following command: 13 | ;;; 14 | ;;; hg log --template "rev: {rev} author: {author} date: {date|shortdate} files:\n{files %'{file}\n'}\n" 15 | ;;; 16 | ;;; The command above uses Mercurial's templating system to format an 17 | ;;; output with each file in the changeset separated by newlines. 18 | ;;; 19 | ;;; Ouput: An sequence of maps where each map represents a change 20 | ;;; entry with the following keyes: 21 | ;;; :entity :date :author :rev 22 | ;;; where 23 | ;;; :entity -> the changed entity as a string 24 | ;;; :date -> commit date as a string 25 | ;;; :author -> as a string 26 | ;;; :rev -> revision from Mercurial 27 | 28 | (def ^:const hg-grammar 29 | "Here's the instaparse grammar for a Mercurial log entry. 30 | Note that we parse the entries one by one (Instaparse memory optimization)." 31 | " 32 | entry = rev author date changes 33 | rev = <'rev: '> #'\\d+' 34 | author = <'author: '> #'.+(?=\\sdate:\\s\\d{4}-)' (* match until the date field *) 35 | date = <'date: '> #'\\d{4}-\\d{2}-\\d{2}' 36 | changes = <'files:'> (file )* 37 | file = #'.+' 38 | ws = #'\\s' 39 | nl = '\\n' 40 | ") 41 | 42 | (def as-common-time-format (tp/time-string-converter-from "YYYY-MM-dd")) 43 | 44 | (def positional-extractors 45 | "Specify a set of functions to extract the parsed values." 46 | {:rev #(get-in % [1 1]) 47 | :author #(get-in % [2 1]) 48 | :date #(as-common-time-format (get-in % [3 1])) 49 | :message (fn [_] "-") 50 | :changes #(rest (get-in % [4]))}) 51 | 52 | (defn parse-log 53 | "Transforms the given input MErcurial log into an 54 | Incanter dataset suitable for the analysis modules." 55 | [input-file-name options] 56 | (hbp/parse-log input-file-name options hg-grammar positional-extractors)) 57 | 58 | (defn parse-read-log 59 | [input-text options] 60 | (hbp/parse-read-log input-text 61 | options 62 | hg-grammar 63 | positional-extractors)) 64 | -------------------------------------------------------------------------------- /src/code_maat/parsers/perforce.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Robert Creager 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.perforce 7 | (:require [code-maat.parsers.time-parser :as tp] 8 | [code-maat.parsers.hiccup-based-parser :as hbp])) 9 | 10 | ;;; This module is responsible for parsing a perforce log file. 11 | ;;; 12 | ;;; Input: a log file generated with the following command: 13 | ;;; 14 | ;;; p4 changes -s submitted -m 5000 //depot/project/... | cut -d ' ' -f 2 | xargs -I commitid -n1 sh -c 'p4 describe -s commitid | grep -v "^\s*$" && echo ""' 15 | ;;; 16 | ;;; Ouput: An sequence of maps where each map represents a change 17 | ;;; entry with the following keyes: 18 | ;;; :entity :date :author :rev 19 | ;;; where 20 | ;;; :entity -> the changed entity as a string 21 | ;;; :date -> commit date as a string 22 | ;;; :author -> as a string 23 | ;;; :rev -> the commit id from perforce 24 | 25 | (def ^:const perforce-grammar 26 | "Here's the instaparse grammar for a perforce change descriptions. 27 | In the current version we only extract basic info on 28 | authors and file modification patterns. 29 | Note that we parse the entries one by one (Instaparse memory optimization)." 30 | " 31 | entry = rev author date
changes 32 | rev = <'Change' ws> #'[\\d]+' 33 | author = <'by' ws> #'[^@]+' <#'[^\\s]+'> 34 | date = <'on' ws> #'\\d{4}/\\d{2}/\\d{2}' 35 | message = (ws* #'.+' nl)+ 36 | header = 'Affected files ...' nl 37 | changes = (file )+ 38 | file = <'... //' #'[^/]+/' #'[^/]+'> #'[^#]+' <#'.+'> 39 | ws = #'\\s' 40 | nl = #'(\\r)?\\n' 41 | ") 42 | 43 | (def as-common-time-format (tp/time-string-converter-from "YYYY/MM/dd")) 44 | 45 | (def positional-extractors 46 | "Specify a set of functions to extract the parsed values." 47 | {:rev #(get-in % [1 1]) 48 | :author #(get-in % [2 1]) 49 | :date #(as-common-time-format (get-in % [3 1])) 50 | :message (fn [_] "") 51 | :changes #(rest (get-in % [4])) 52 | }) 53 | 54 | (defn parse-log 55 | "Transforms the given input perforce log into an 56 | Incanter dataset suitable for the analysis modules." 57 | [input-file-name options] 58 | (hbp/parse-log 59 | input-file-name 60 | options 61 | perforce-grammar 62 | positional-extractors)) 63 | 64 | (defn parse-read-log 65 | [input-text options] 66 | (hbp/parse-read-log 67 | input-text 68 | options 69 | perforce-grammar 70 | positional-extractors)) 71 | -------------------------------------------------------------------------------- /src/code_maat/parsers/svn.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.svn 7 | (:require [code-maat.parsers.time-parser :as tp] 8 | [clojure.data.zip.xml :as zip-xml] 9 | [clojure.string :as s])) 10 | 11 | ;;; This module contains functionality for parsing a generated SVN log 12 | ;;; into a map suitable for the supported analysis. 13 | ;;; 14 | ;;; Input: A SVN log on XML format. 15 | ;;; 16 | ;;; Ouput: A seq of maps with the following keys: 17 | ;;; :entity :action :date :author :rev 18 | 19 | (defn zip->log-entries [zipped] 20 | (zip-xml/xml-> zipped :logentry)) 21 | 22 | (defn- make-extractor [logentry] 23 | (partial zip-xml/xml1-> logentry)) 24 | 25 | (defn- group-file-with-action [entry] 26 | (let [entity-name (s/trimr (zip-xml/text entry)) 27 | svn-action (zip-xml/attr entry :action)] 28 | [entity-name svn-action])) 29 | 30 | (defn- extract-modified-files 31 | "Extracts all modified files from the given logentry." 32 | [logentry] 33 | (let [paths (zip-xml/xml-> logentry :paths :path)] 34 | (map group-file-with-action paths))) 35 | 36 | (def as-common-time-format (tp/time-string-converter-from "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'")) 37 | 38 | (defn as-rows 39 | "Transforms the given svn logentry to a seq of rows containing 40 | the modification data for each entity." 41 | [svn-logentry] 42 | (let [extractor (make-extractor svn-logentry) 43 | entities (extract-modified-files svn-logentry) 44 | date (extractor :date zip-xml/text) 45 | author (extractor :author zip-xml/text) 46 | revision (extractor (zip-xml/attr :revision))] 47 | (map (fn [[entity action]] 48 | {:entity entity 49 | :date (as-common-time-format date) 50 | :author author 51 | :action action 52 | :rev revision}) 53 | entities))) 54 | 55 | (defn- parse 56 | [zipped parse-options] 57 | (->> 58 | zipped 59 | zip->log-entries 60 | (mapcat as-rows))) 61 | 62 | (defn zip->modification-sets 63 | "Transforms the given zipped svn log into a map 64 | of modification data. 65 | The map contains the following rows: 66 | :entity :action :date :author :rev" 67 | ([zipped] 68 | (zip->modification-sets zipped {})) 69 | ([zipped parse-options] 70 | (parse zipped parse-options))) 71 | -------------------------------------------------------------------------------- /src/code_maat/parsers/tfs.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2016 Ryan Coy 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.tfs 7 | (:require [clojure.string :as str] 8 | [code-maat.parsers.time-parser :as tp] 9 | [code-maat.parsers.hiccup-based-parser :as hbp])) 10 | 11 | ;;; This module is responsible for parsing a TFS log file. 12 | ;;; 13 | ;;; Input: a log file generated with the following command: 14 | ;;; 15 | ;;; tf hist {path} /noprompt /format:detailed /recursive 16 | ;;; 17 | ;;; Output: A sequence of maps where each map represents a change entry 18 | ;;; from the version-control log: 19 | ;;; :entity :date :author :rev 20 | ;;; where 21 | ;;; :entity -> the changed entity as a string 22 | ;;; :date -> changeset date as a string 23 | ;;; :author -> as a string 24 | ;;; :rev -> the changeset version from TFS 25 | 26 | ;;; Sample input where each commit is separated by the dividing line 27 | ;;; 28 | ;;; ----------------------------------------------------------------------------------------------------------------------- 29 | ;;; Changeset: 1 30 | ;;; User: Ryan Coy 31 | ;;; Date: Thursday, March 17, 2016 12:44:13 PM 32 | ;;; 33 | ;;; Comment: 34 | ;;; Created team project folder $/MyProject via the Team Project Creation Wizard 35 | ;;; 36 | ;;; Items: 37 | ;;; add $/MyProject 38 | 39 | (def ^:const tfs-grammar 40 | "This is the instaparse grammar for a TFS entry. 41 | Things get parsed one-by-one for memory optimization 42 | TFS doesn't give us lines added/deleted, so we only get the core metrics" 43 | " 44 | changeset = changelog userinfo timestamp comment changes 45 | sep = '-'* 46 | = <'Changeset: '> id 47 | id = #'[\\d]+' 48 | = <'User: '> author 49 | author = #'.+' 50 | = <'Checked in by: '> #'.+' 51 | = <'Date: '> date 52 | date = #'.+' 53 | = <'Comment:'> message 54 | message = line* 55 | = <' '> #'[\\S ]+' 56 | changes = <'Items:'> file* 57 | file = <'$'> #'.+' 58 | action = #'[a-zA-Z, ]+' 59 | = <'Check-in Notes:'> line* 60 | = <'Policy Warnings:'> line* 61 | ws = #'\\s' 62 | nl = '\\r'?'\\n'") 63 | 64 | ;;; TFS logs come with whitespace inside individual entries 65 | ;;; We remove this because the main parser uses blank lines 66 | ;;; to identify the break between entries 67 | 68 | (defn tfs-preparse 69 | "Removes whitespace lines inside individual changeset log entries 70 | char-array is used as a java.io.reader compatible input since we won't 71 | be giving it an actual file" 72 | [input-file-text] 73 | (char-array (str/replace input-file-text 74 | #"\r?\n\s*\r?\n(?!-)" 75 | "\r\n"))) 76 | 77 | ;;; This matches the default EN-US format: 78 | ;;; Wednesday, March 23, 2016 7:34:43 PM 79 | (def as-common-time-format (tp/time-string-converter-from "E, MMMM d, yyyy H:m:s a")) 80 | 81 | ;;; The date parsing attempts to parse some common formats 82 | (def positional-extractors 83 | "Specify a set of functions to extract the parsed values." 84 | {:rev #(get-in % [1 1]) 85 | :date (fn [entry] (let [date-string (get-in entry [3 1])] 86 | (try 87 | (as-common-time-format date-string) 88 | (catch Exception e (throw (IllegalArgumentException. (str "Unsupported TFS Date Format: " date-string))))))) 89 | :author #(get-in % [2 1]) 90 | :changes #(rest (get-in % [5])) 91 | :message (fn [entry] (let [message (get-in entry [4])] 92 | (str/join "\n" (rest message))))}) 93 | 94 | (defn parse-log 95 | "Transforms the given input TFS log into an 96 | Incanter dataset suitable for the analysis modules." 97 | [input-file-name options] 98 | (hbp/parse-log (tfs-preparse (slurp input-file-name)) 99 | options 100 | tfs-grammar 101 | positional-extractors)) 102 | 103 | (defn parse-read-log 104 | [input-text options] 105 | (hbp/parse-read-log (clojure.string/join (tfs-preparse input-text)) 106 | options 107 | tfs-grammar 108 | positional-extractors)) 109 | -------------------------------------------------------------------------------- /src/code_maat/parsers/time_parser.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.time-parser 7 | (:require [clj-time.format :as tf])) 8 | 9 | (def internal-time-format (tf/formatter "YYYY-MM-dd")) 10 | 11 | (defn time-string-converter-from 12 | [format] 13 | (let [time-format (tf/formatter format)] 14 | (fn [time-as-string] 15 | (->> 16 | time-as-string 17 | (tf/parse time-format) 18 | (tf/unparse internal-time-format))))) 19 | 20 | (defn time-parser-from 21 | [format] 22 | (let [time-format (tf/formatter format)] 23 | (fn [time-as-string] 24 | (tf/parse time-format time-as-string)))) 25 | -------------------------------------------------------------------------------- /src/code_maat/parsers/xml.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.xml 7 | (:require [clojure.xml :as xml] 8 | [clojure.zip :as zip])) 9 | 10 | (defn file->zip 11 | "Parses the given xml-file into a zipper data structure." 12 | [xml-file] 13 | (zip/xml-zip (xml/parse xml-file))) 14 | 15 | (defn string->zip 16 | "Parses the given string into a zipper data structure." 17 | [s] 18 | (zip/xml-zip (xml/parse (new org.xml.sax.InputSource 19 | (new java.io.StringReader s))))) 20 | -------------------------------------------------------------------------------- /src/code_maat/test/data_driven.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.test.data-driven 7 | (:require [clojure.test :refer [deftest]])) 8 | 9 | (defmacro def-dd-test 10 | "Defines a data-driven test based on clojure.test/deftest. 11 | Note that this macro is more of a starting point solving 12 | my immediate needs than a good idea. 13 | 14 | Example: 15 | (def-dd-test gittest 16 | [ddval [git svn]] 17 | (is (= (analysis ddval) 18 | expected-output))) 19 | 20 | The code above will generate two deftest, one for each supplied value. 21 | The generated code in each deftest will evaluate the body (is (= ...)) with the 22 | symbol ddval bound to 'git' for the first deftest and 'svn' for the second deftest." 23 | [name args & body] 24 | (let [[param values] args] 25 | `(do 26 | ~@(for [i (range (count values))] 27 | (let [value (nth values i) 28 | name (symbol (str name "-" value))] 29 | `(deftest ~name 30 | (let [~param ~value] 31 | ~@body))))))) 32 | -------------------------------------------------------------------------------- /test/code_maat/analysis/authors_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.authors-test 7 | (:require [code-maat.analysis.authors :as authors] 8 | [code-maat.analysis.test-data :as test-data] 9 | [incanter.core :as incanter]) 10 | (:use clojure.test)) 11 | 12 | (deftest deduces-all-authors 13 | (is (= (into #{} (authors/all test-data/vcsd)) 14 | #{"apt" "jt"}))) 15 | 16 | (deftest gives-all-authors-of-specified-module 17 | (is (= (incanter/to-list (authors/of-module "A" test-data/vcsd)) 18 | [["apt"] ["jt"]])) 19 | (is (= (incanter/to-list (authors/of-module "B" test-data/vcsd)) 20 | [["apt"]]))) 21 | 22 | (deftest sorts-entities-on-max-number-of-authors 23 | (is (= (test-data/content-of (authors/by-count 24 | test-data/vcsd 25 | test-data/options-with-low-thresholds)) 26 | [{:n-authors 2 :entity "A" :n-revs 3} 27 | {:n-authors 1 :entity "B" :n-revs 1}]))) 28 | 29 | (deftest sorts-order-is-optional 30 | (is (= (test-data/content-of (authors/by-count 31 | test-data/vcsd 32 | test-data/options-with-low-thresholds 33 | :asc)) 34 | [{:n-authors 1 :entity "B" :n-revs 1} 35 | {:n-authors 2 :entity "A" :n-revs 3}]))) 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/code_maat/analysis/churn_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.churn-test 7 | (:require [code-maat.analysis.churn :as churn] 8 | [code-maat.dataset.dataset :as ds] 9 | [incanter.core :as incanter]) 10 | (:use clojure.test)) 11 | 12 | (def ^:const options {}) 13 | 14 | (ds/def-ds incomplete 15 | [{:entity "A" :rev 1 :author "at" :date "2013-11-10"} 16 | {:entity "B" :rev 2 :author "ta" :date "2013-11-11"}]) 17 | 18 | (ds/def-ds single-author 19 | [{:entity "Same" :rev 1 :author "single" :date "2013-11-10" :loc-added "10" :loc-deleted "1"} 20 | {:entity "Same" :rev 2 :author "single" :date "2013-11-11" :loc-added "20" :loc-deleted "2"} 21 | {:entity "Same" :rev 3 :author "single" :date "2013-11-11" :loc-added "2" :loc-deleted "0"}]) 22 | 23 | ;; Of course, an author doesn't have to add lines. In that case we 24 | ;; need to take care since we get a total of zero => special case. 25 | (ds/def-ds only-removed-lines 26 | [{:entity "Same" :rev 1 :author "single" :date "2013-11-10" :loc-added "0" :loc-deleted "1"}]) 27 | 28 | (ds/def-ds only-added-lines 29 | [{:entity "Same" :rev 1 :author "single" :date "2013-11-10" :loc-added "1" :loc-deleted "0"}]) 30 | 31 | (ds/def-ds simple 32 | [{:entity "B" :rev 2 :author "ta" :date "2013-11-11" :loc-added "20" :loc-deleted "2"} 33 | {:entity "A" :rev 1 :author "at" :date "2013-11-10" :loc-added "10" :loc-deleted "1"} 34 | {:entity "B" :rev 1 :author "at" :date "2013-11-10" :loc-added "1" :loc-deleted "1"} 35 | {:entity "B" :rev 3 :author "at" :date "2013-11-11" :loc-added "2" :loc-deleted "0"}]) 36 | 37 | (ds/def-ds same-author 38 | [{:entity "A" :rev 1 :author "at" :date "2013-11-10" :loc-added "10" :loc-deleted "1"} 39 | {:entity "A" :rev 2 :author "at" :date "2013-11-11" :loc-added "2" :loc-deleted "5"} 40 | {:entity "A" :rev 3 :author "xy" :date "2013-11-11" :loc-added "7" :loc-deleted "1"} 41 | {:entity "A" :rev 4 :author "xy" :date "2013-11-11" :loc-added "8" :loc-deleted "2"}]) 42 | 43 | (ds/def-ds with-binary 44 | [{:entity "binary" :rev 1 :author "at" :date "2013-11-10" :loc-added "-" :loc-deleted "-"}]) 45 | 46 | (deftest throws-error-on-missing-modification-info 47 | "Some VCS (e.g. hg) don't provide the necessary metrics. 48 | In case a churn analysis is requested on such incomplete 49 | data we want to detect it early." 50 | (is (thrown? IllegalArgumentException 51 | (churn/absolutes-trend incomplete options)))) 52 | 53 | (deftest calculates-absolute-churn-by-date 54 | (is (= (churn/absolutes-trend simple options) 55 | (ds/-dataset [:date :added :deleted :commits] 56 | [{:date "2013-11-10" :added 11 :deleted 2 :commits 1} 57 | {:date "2013-11-11" :added 22 :deleted 2 :commits 2}])))) 58 | 59 | (deftest binaries-are-counted-as-zero-churn 60 | "There are simply no statistics from the VCS for these." 61 | (is (= (churn/absolutes-trend with-binary options) 62 | (ds/-dataset [:date :added :deleted :commits] 63 | [{:date "2013-11-10" :added 0 :deleted 0 :commits 1}])))) 64 | 65 | (deftest calculates-churn-by-author 66 | "Get an overview of individual contributions." 67 | (is (= (churn/by-author simple options) 68 | (ds/-dataset [:author :added :deleted :commits] 69 | [{:author "at" :added 13 :deleted 2 :commits 2} 70 | {:author "ta" :added 20 :deleted 2 :commits 1}])))) 71 | 72 | (deftest calculates-churn-by-entity 73 | "Identify entities with the highest churn rate." 74 | (is (= (churn/by-entity simple options) 75 | (ds/-dataset [:entity :added :deleted :commits] 76 | [{:entity "B" :added 23 :deleted 3 :commits 3} 77 | {:entity "A" :added 10 :deleted 1 :commits 1}])))) 78 | 79 | (deftest calculates-author-ownership-from-churn 80 | (is (= (churn/as-ownership simple options) 81 | (ds/-dataset [:entity :author :added :deleted] 82 | [["A" "at" 10 1] 83 | ["B" "ta" 20 2] 84 | ["B" "at" 3 1]])))) 85 | 86 | (deftest sums-ownership-churn-for-same-author 87 | "We want an aggregated number when the same author makes multiple mods." 88 | (is (= (churn/as-ownership same-author options) 89 | (ds/-dataset [:entity :author :added :deleted] 90 | [["A" "at" 12 6] 91 | ["A" "xy" 15 3]])))) 92 | 93 | ;;; Tests of the main developer algorithm 94 | 95 | (defn- as-main-dev-ds 96 | [v] 97 | (ds/-dataset [:entity :main-dev :added :total-added :ownership] v)) 98 | 99 | (deftest identifies-single-main-developer 100 | "A main developer is the one who conributed most code. 101 | If there's only one, single developer it's the obvious owner." 102 | (is (= (churn/by-main-developer single-author options) 103 | (as-main-dev-ds [["Same" "single" 32 32 1.0]])))) 104 | 105 | (deftest identifies-main-developer-on-shared-entities 106 | (is (= (churn/by-main-developer same-author options) 107 | (as-main-dev-ds [["A" "xy" 15 27 0.56]])))) 108 | 109 | (deftest ownership-is-none-without-added-lines 110 | (is (= (churn/by-main-developer only-removed-lines options) 111 | (as-main-dev-ds [["Same" "single" 0 0 0.00]])))) 112 | 113 | ;;; Tests of main developer algorithms for a refactoring developer 114 | 115 | (defn- as-refactor-ds 116 | [v] 117 | (ds/-dataset [:entity :main-dev :removed :total-removed :ownership] v)) 118 | 119 | (deftest identifies-refactoring-developer-on-shared-entities 120 | (is (= (churn/by-refactoring-main-developer same-author options) 121 | (as-refactor-ds [["A" "at" 6 9 0.67]])))) 122 | 123 | (deftest ownership-is-none-without-removed-lines 124 | (is (= (churn/by-refactoring-main-developer only-added-lines options) 125 | (as-refactor-ds [["Same" "single" 0 0 0.00]])))) 126 | -------------------------------------------------------------------------------- /test/code_maat/analysis/code_age_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.code-age-test 7 | (:require [code-maat.analysis.code-age :as analysis] 8 | [code-maat.analysis.test-data :as td] 9 | [incanter.core :as incanter] 10 | [code-maat.dataset.dataset :as ds]) 11 | (:use clojure.test)) 12 | 13 | (def ^:const vcs [{:entity "A" :rev 1 :date "2013-12-25"} 14 | {:entity "B" :rev 1 :date "2013-12-31"} 15 | {:entity "A" :rev 2 :date "2014-02-28"} 16 | {:entity "A" :rev 3 :date "2014-04-05"}]) 17 | (def ^:const vcsd (incanter/to-dataset vcs)) 18 | 19 | (defn- as-now 20 | "To make the tests deterministic we need to specify what 21 | _now_ really means. This is done by a comand line argument." 22 | [fixed-date] 23 | {:age-time-now fixed-date}) 24 | 25 | (defn- as-age-ds 26 | [result] 27 | (ds/-dataset [:entity :age-months] result)) 28 | 29 | (deftest calculates-age-by-last-modification-date 30 | (is (= (analysis/by-age vcsd (as-now "2014-04-06")) 31 | (as-age-ds [["A" 0] ["B" 3]])))) 32 | 33 | (deftest code-gets-older-as-time-passes-by 34 | (testing "One month into the future" 35 | (is (= (analysis/by-age vcsd (as-now "2014-05-06")) 36 | (as-age-ds [["A" 1] ["B" 4]])))) 37 | (testing "A year into the future" 38 | (is (= (analysis/by-age vcsd (as-now "2015-04-06")) 39 | (as-age-ds [["A" 12] ["B" 15]]))))) 40 | 41 | ;;; This is tricky - if we move back in time we need to 42 | ;;; ignore all commits that happened after the given time. 43 | (deftest code-was-younger-in-the-past 44 | (testing "One month in the past" 45 | (is (= (analysis/by-age vcsd (as-now "2014-03-06")) 46 | (as-age-ds [["A" 0] ["B" 2]])))) 47 | (testing "Before the B module was introduced (should be ignored)" 48 | (is (= (analysis/by-age vcsd (as-now "2013-12-26")) 49 | (as-age-ds [["A" 0]]))))) 50 | 51 | -------------------------------------------------------------------------------- /test/code_maat/analysis/commit_messages_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.commit-messages-test 7 | (:require [code-maat.analysis.commit-messages :as c] 8 | [code-maat.analysis.test-data :as td] 9 | [code-maat.dataset.dataset :as ds] 10 | [incanter.core :as incanter]) 11 | (:use clojure.test)) 12 | 13 | (defn- as-option 14 | [word] 15 | {:expression-to-match word}) 16 | 17 | (deftest identifies-matching-words 18 | (is (= (c/by-word-frequency td/vcsd (as-option "change")) 19 | (ds/-dataset [:entity :matches] 20 | [["A" 3] ["B" 1]]))) 21 | (is (= (c/by-word-frequency td/vcsd (as-option "Third")) 22 | (ds/-dataset [:entity :matches] 23 | [["A" 1]]))) 24 | (is (= (c/by-word-frequency td/vcsd (as-option "no match for this")) 25 | (ds/-dataset [:entity :matches] 26 | [])))) 27 | 28 | (defn- run-messages-analysis 29 | [commits] 30 | (-> commits 31 | incanter/to-dataset 32 | (c/by-word-frequency (as-option "change")))) 33 | 34 | (deftest detects-absent-message-fields ; not all supported version-control formats include a commit message 35 | (testing "No commit messages triggers exception" 36 | (is (thrown? IllegalArgumentException 37 | (run-messages-analysis [{:author "apt" :entity "A" :rev 1 :message "-"} 38 | {:author "apt" :entity "B" :rev 2 :message "-"}])))) 39 | (testing "Empy dataset is valid" 40 | (is (= (run-messages-analysis []) 41 | (ds/-dataset [:entity :matches] 42 | [])))) 43 | (testing "Valid, if any commit has a message" 44 | (is (= (run-messages-analysis [{:author "apt" :entity "A" :rev 1 :message "-"} 45 | {:author "apt" :entity "B" :rev 2 :message "some change message"}]) 46 | (ds/-dataset [:entity :matches] 47 | [["B" 1]]))))) 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/code_maat/analysis/communication_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.communication-test 7 | (:require [code-maat.analysis.communication :as communication] 8 | [code-maat.dataset.dataset :as ds]) 9 | (:use clojure.test)) 10 | 11 | (def ^:const options {}) 12 | 13 | (ds/def-ds sharing-authors 14 | [{:entity "A" :rev 1 :author "at" :date "2013-11-10"} 15 | {:entity "A" :rev 2 :author "jt" :date "2013-11-11"} 16 | {:entity "A" :rev 3 :author "ap" :date "2013-11-15"} 17 | {:entity "B" :rev 4 :author "at" :date "2013-11-23"} 18 | {:entity "B" :rev 5 :author "jt" :date "2013-11-23"}]) 19 | 20 | (deftest calculates-communication-needs-for-shared-authorship 21 | (is (= (communication/by-shared-entities sharing-authors options) 22 | (ds/-dataset [:author :peer :shared :average :strength] 23 | [["jt" "at" 2 2 100 ] 24 | ["at" "jt" 2 2 100 ] 25 | ["jt" "ap" 1 2 50] 26 | ["at" "ap" 1 2 50] 27 | ["ap" "jt" 1 2 50] 28 | ["ap" "at" 1 2 50]])))) 29 | -------------------------------------------------------------------------------- /test/code_maat/analysis/coupling_algos_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.coupling-algos-test 7 | (:require [code-maat.analysis.coupling-algos :as coupling] 8 | [code-maat.analysis.test-data :as test-data] 9 | [incanter.core :as incanter]) 10 | (:use clojure.test)) 11 | 12 | (def ^:const single-entity-commit 13 | [{:entity "This/is/a/single/entity" :rev 1}]) 14 | 15 | (def ^:const one-revision 16 | [{:entity "A" :rev 1} 17 | {:entity "B" :rev 1} 18 | {:entity "C" :rev 1}]) 19 | 20 | (def ^:const single (incanter/to-dataset one-revision)) 21 | 22 | (def ^:const coupled 23 | [{:entity "A" :rev 1} 24 | {:entity "B" :rev 1} 25 | {:entity "C" :rev 1} 26 | {:entity "A" :rev 2} 27 | {:entity "B" :rev 2}]) 28 | 29 | (def ^:const multiple (incanter/to-dataset coupled)) 30 | 31 | (def ^:const expected-multiple-co-changes 32 | [[["A" "A"] ["A" "B"] ["A" "C"] ["B" "B"] ["B" "C"] ["C" "C"]] 33 | [["A" "A"] ["A" "B"] ["B" "B"]]]) 34 | 35 | (deftest identifies-couples-in-a-revision 36 | "Note that this step in the algorithm maintains identity couples. 37 | We use them later to sum-up the the total number of revisions of 38 | a module." 39 | (is (= (coupling/co-changing-by-revision single test-data/options-with-low-thresholds) 40 | [[["A" "A"] ["A" "B"] ["A" "C"] ["B" "B"] ["B" "C"] ["C" "C"]]]))) 41 | 42 | (deftest identifies-couples-in-multiple-revisions 43 | (is (= (coupling/co-changing-by-revision multiple test-data/options-with-low-thresholds) 44 | expected-multiple-co-changes))) 45 | 46 | (deftest calculates-coupling-frequencies 47 | (is (= (coupling/coupling-frequencies expected-multiple-co-changes) 48 | [[["A" "B"] 2] [["A" "C"] 1] [["B" "C"] 1]]))) 49 | 50 | (deftest calculates-module-change-freqs 51 | (is (= (coupling/module-by-revs expected-multiple-co-changes) 52 | {"A" 2, "B" 2, "C" 1}))) 53 | -------------------------------------------------------------------------------- /test/code_maat/analysis/effort_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.effort-test 7 | (:require [code-maat.analysis.effort :as effort] 8 | [code-maat.dataset.dataset :as ds]) 9 | (:use clojure.test)) 10 | 11 | (def ^:const options {}) 12 | 13 | (ds/def-ds single-effort 14 | [{:entity "A" :rev 1 :author "at" :date "2013-11-10"} 15 | {:entity "B" :rev 2 :author "at" :date "2013-11-11"} 16 | {:entity "B" :rev 3 :author "at" :date "2013-11-15"}]) 17 | 18 | (ds/def-ds multi-effort 19 | [{:entity "Z" :rev 4 :author "zt" :date "2013-11-15"} 20 | {:entity "Z" :rev 5 :author "xy" :date "2013-11-15"} 21 | {:entity "Z" :rev 6 :author "xy" :date "2013-11-16"} 22 | 23 | {:entity "C" :rev 4 :author "zt" :date "2013-11-15"} 24 | {:entity "C" :rev 5 :author "zt" :date "2013-11-15"} 25 | 26 | {:entity "A" :rev 1 :author "at" :date "2013-11-10"} 27 | {:entity "A" :rev 2 :author "xy" :date "2013-11-11"} 28 | {:entity "A" :rev 3 :author "zt" :date "2013-11-15"} 29 | {:entity "A" :rev 4 :author "zt" :date "2013-11-15"} 30 | {:entity "A" :rev 5 :author "xy" :date "2013-11-15"} 31 | {:entity "A" :rev 6 :author "xy" :date "2013-11-16"}]) 32 | 33 | (deftest calculates-effort-for-single-author 34 | (is (= (effort/as-revisions-per-author single-effort options) 35 | (ds/-dataset [:entity :author :author-revs :total-revs] 36 | [["A" "at" 1 1] 37 | ["B" "at" 2 2]])))) 38 | 39 | (deftest calculates-effort-for-multiple-authors 40 | (testing "With multiple authors, the effort is sorted in descending order (main author first)" 41 | (is (= (effort/as-revisions-per-author multi-effort options) 42 | (ds/-dataset [:entity :author :author-revs :total-revs] 43 | [["A" "xy" 3 6] 44 | ["A" "zt" 2 6] 45 | ["A" "at" 1 6] 46 | ["C" "zt" 2 2] 47 | ["Z" "xy" 2 3] 48 | ["Z" "zt" 1 3]]))))) 49 | 50 | (deftest calculates-entity-fragmentation-for-single-author 51 | "The fractal value is a measurement of how 52 | distributed the effort on a specific entity is." 53 | (is (= (effort/as-entity-fragmentation single-effort options) 54 | (ds/-dataset [:entity :fractal-value :total-revs] 55 | [["B" 0.00 2] 56 | ["A" 0.00 1]])))) 57 | 58 | (deftest calculates-entity-fragmentation-for-multiple-authors 59 | (is (= (effort/as-entity-fragmentation multi-effort options) 60 | (ds/-dataset [:entity :fractal-value :total-revs] 61 | [["A" 0.61 6] 62 | ["Z" 0.44 3] 63 | ["C" 0.0 2]])))) 64 | 65 | (ds/def-ds shared-effort 66 | [{:entity "A" :rev 1 :author "zt" :date "2013-11-10"} 67 | {:entity "A" :rev 2 :author "at" :date "2013-11-11"} 68 | {:entity "A" :rev 3 :author "at" :date "2013-11-15"} 69 | {:entity "B" :rev 4 :author "xx" :date "2013-11-15"} 70 | {:entity "C" :rev 5 :author "x1" :date "2013-11-16"} 71 | {:entity "C" :rev 6 :author "x2" :date "2013-11-16"}]) 72 | 73 | (deftest identifies-main-developer-by-revisions 74 | (is (= (effort/as-main-developer-by-revisions shared-effort options) 75 | (ds/-dataset [:entity :main-dev :added :total-added :ownership] 76 | [["A" "at" 2 3 0.67] 77 | ["B" "xx" 1 1 1.0] 78 | ["C" "x1" 1 2 0.5]])))) 79 | -------------------------------------------------------------------------------- /test/code_maat/analysis/entities_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.entities-test 7 | (:require [code-maat.analysis.entities :as entities] 8 | [code-maat.analysis.test-data :as test-data]) 9 | (:use clojure.test)) 10 | 11 | (deftest deduces-all-modified-entities 12 | (= (entities/all test-data/vcsd) 13 | #{"apt" "jt" "xy"})) 14 | 15 | (deftest sorts-entities-on-number-of-revisions 16 | (is (= (test-data/content-of (entities/by-revision 17 | test-data/vcsd 18 | test-data/options-with-low-thresholds)) 19 | [{:n-revs 3 :entity "A"} 20 | {:n-revs 1, :entity "B"}]))) 21 | 22 | (deftest calculates-revisions-of-specific-entites 23 | (let [rg (entities/by-revision 24 | test-data/vcsd 25 | test-data/options-with-low-thresholds)] 26 | (is (= (entities/revisions-of "A" rg) 27 | 3)) 28 | (is (= (entities/revisions-of "B" rg) 29 | 1)))) -------------------------------------------------------------------------------- /test/code_maat/analysis/logical_coupling_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.logical-coupling-test 7 | (:require [code-maat.analysis.logical-coupling :as coupling] 8 | [code-maat.analysis.test-data :as test-data] 9 | [incanter.core :as incanter]) 10 | (:use clojure.test)) 11 | 12 | (def ^:const single-entity-commit 13 | [{:entity "This/is/a/single/entity" :rev 1}]) 14 | 15 | (def ^:const one-revision 16 | [{:entity "A" :rev 1} 17 | {:entity "B" :rev 1} 18 | {:entity "C" :rev 1}]) 19 | 20 | (def ^:const revd (incanter/to-dataset one-revision)) 21 | 22 | (def ^:const coupled 23 | [{:entity "A" :rev 1} 24 | {:entity "B" :rev 1} 25 | {:entity "C" :rev 1} 26 | {:entity "A" :rev 2} 27 | {:entity "B" :rev 2}]) 28 | 29 | (def ^:const coupledd (incanter/to-dataset coupled)) 30 | 31 | (def ^:const revd (incanter/to-dataset one-revision)) 32 | 33 | (def ^:private default-options test-data/options-with-low-thresholds) 34 | 35 | (deftest calculates-coupling-by-degree 36 | (is (= (incanter/to-list (coupling/by-degree 37 | coupledd 38 | default-options)) 39 | ;; :entity :coupled :degree :average-revs 40 | [["A" "B" 100 2] 41 | ["A" "C" 66 2] 42 | ["B" "C" 66 2]]))) 43 | 44 | (deftest outputs-verbose-details-when-prompted-to 45 | (is (= (incanter/to-list (coupling/by-degree 46 | coupledd 47 | (assoc default-options :verbose-results true))) 48 | ;; :entity :coupled :degree :average-revs revs1 revs2 shared-revs 49 | [["A" "B" 100 2 2 2 2] 50 | ["A" "C" 66 2 2 1 1] 51 | ["B" "C" 66 2 2 1 1]]))) 52 | 53 | (deftest gives-empty-result-for-single-change-set-with-single-entity 54 | "A single change set with a single entity (boundary case)" 55 | (is (= (incanter/to-list (coupling/by-degree 56 | (incanter/to-dataset single-entity-commit) 57 | test-data/options-with-low-thresholds)) 58 | []))) 59 | -------------------------------------------------------------------------------- /test/code_maat/analysis/math_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2016 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.math-test 7 | (:require [code-maat.analysis.math :as m]) 8 | (:use clojure.test)) 9 | 10 | (deftest centi-float-precision 11 | "Test correct centi-float-precision." 12 | (is (= 1.0 (m/ratio->centi-float-precision 1.000))) 13 | (is (= 0.5 (m/ratio->centi-float-precision 0.5))) 14 | (is (= 0.67 (m/ratio->centi-float-precision 2/3))) 15 | (is (= 0.83 (m/ratio->centi-float-precision 5/6)))) 16 | -------------------------------------------------------------------------------- /test/code_maat/analysis/sum_of_coupling_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.sum-of-coupling-test 7 | (:require [code-maat.analysis.sum-of-coupling :as coupling] 8 | [code-maat.analysis.test-data :as test-data] 9 | [incanter.core :as incanter]) 10 | (:use clojure.test)) 11 | 12 | (def ^:const coupled 13 | [{:entity "A" :rev 1} 14 | {:entity "B" :rev 1} 15 | {:entity "C" :rev 1} 16 | {:entity "A" :rev 2} 17 | {:entity "B" :rev 2}]) 18 | 19 | (def ^:const multiple (incanter/to-dataset coupled)) 20 | 21 | (deftest measures-coupling-by-entity 22 | (is (= (coupling/as-soc 23 | multiple 24 | test-data/options-with-low-thresholds) 25 | [["A" 3] ["B" 3] ["C" 2]]))) 26 | -------------------------------------------------------------------------------- /test/code_maat/analysis/test_data.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.analysis.test-data 7 | (:require [incanter.core :as incanter])) 8 | 9 | (def ^:const vcs [{:author "apt" :entity "A" :rev 1 :message "Some change"} 10 | {:author "apt" :entity "B" :rev 1 :message "Another change"} 11 | {:author "apt" :entity "A" :rev 2 :message "Second change"} 12 | {:author "jt" :entity "A" :rev 3 :message "Third change"}]) 13 | (def ^:const vcsd (incanter/to-dataset vcs)) 14 | 15 | ;;; Defines a dataset with a single entry to test one border case. 16 | (def ^:const single-vcs [{:author "apt" :entity "A" :rev 1}]) 17 | (def ^:const single-vcsd (incanter/to-dataset single-vcs)) 18 | 19 | (def ^:const empty-vcsd (incanter/to-dataset [])) 20 | 21 | (defn content-of [ds] 22 | (:rows (incanter/sel 23 | ds 24 | :rows :all))) 25 | 26 | (def ^:const options-with-low-thresholds 27 | "Typically given as cmd line args" 28 | {:min-revs 1 29 | :min-shared-revs 1 30 | :min-coupling 50 31 | :max-coupling 100 32 | :max-changeset-size 10}) 33 | -------------------------------------------------------------------------------- /test/code_maat/app/cmd_line_test.clj: -------------------------------------------------------------------------------- 1 | (ns code-maat.app.cmd-line-test 2 | (:require [clojure.test :refer :all] 3 | [clojure.tools.cli :as cli] 4 | [code-maat.cmd-line :refer :all])) 5 | 6 | 7 | (deftest test-argument-parsing 8 | (testing "simple cmd line parsing" 9 | (let [args ["-l some_file.log"] 10 | parsed-options (cli/parse-opts args cli-options)] 11 | 12 | (is (nil? (:errors parsed-options)))))) 13 | -------------------------------------------------------------------------------- /test/code_maat/app/day_coupled_entities_git.txt: -------------------------------------------------------------------------------- 1 | [2] APT 2013-02-08 git: authors and revisions implemented 2 | 1 2 /Infrastrucure/Network/Connection.cs 3 | 3 4 /Presentation/Status/ClientPresenter.cs 4 | 5 | [1] XYZ 2013-02-08 Report connection status 6 | 18 2 /Infrastrucure/Network/TcpConnection.cs 7 | -------------------------------------------------------------------------------- /test/code_maat/app/grouper_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | 4 | (ns code-maat.app.grouper-test 5 | (:require [code-maat.app.grouper :as g] 6 | [incanter.core :as incanter]) 7 | (:use clojure.test)) 8 | 9 | (def ^:const single-group-spec 10 | "/some/path => G1") 11 | 12 | (def ^:const multi-group-spec 13 | "/some/path => G1 14 | /another/path => G2") 15 | 16 | (def ^:const multi-regexp-group-spec 17 | "^/some/path_\\w+_group1$ => G1 18 | ^/another/path_\\w+_group2$ => G2") 19 | 20 | (def ^:const multi-mixed-group-spec 21 | "/some/path => G1 22 | ^/another/path/\\.*$ => G2") 23 | 24 | (defn- comparable-group-spec 25 | "Normalize the string (regex or text) that 26 | make up the path expression" 27 | [s] 28 | (map #(update % :path str) s)) 29 | 30 | (defn- comparable-group-spec-for 31 | "Compare the hash maps that are generated 32 | by the group specification" 33 | [text] 34 | (-> text 35 | g/text->group-specification 36 | comparable-group-spec)) 37 | 38 | (deftest parses-specification 39 | (testing "Single group" 40 | (is (= (comparable-group-spec-for single-group-spec) 41 | (comparable-group-spec [{:path "^/some/path/" 42 | :name "G1"}])))) 43 | 44 | (testing "Multiple text groups" 45 | (is (= (comparable-group-spec-for multi-group-spec) 46 | (comparable-group-spec [{:path "^/some/path/" 47 | :name "G1"} 48 | {:path "^/another/path/" 49 | :name "G2"}])))) 50 | 51 | (testing "Multiple regexp groups" 52 | (is (= (comparable-group-spec-for multi-regexp-group-spec) 53 | (comparable-group-spec [{:path "^/some/path_\\w+_group1$" 54 | :name "G1"} 55 | {:path "^/another/path_\\w+_group2$" 56 | :name "G2"}])))) 57 | 58 | (testing "Multiple text and regexp groups" 59 | (is (= (comparable-group-spec-for multi-mixed-group-spec) 60 | (comparable-group-spec [{:path "^/some/path/" 61 | :name "G1"} 62 | {:path "^/another/path/\\.*$" 63 | :name "G2"}])))) 64 | 65 | (testing "No groups" 66 | (is (= (g/text->group-specification "") 67 | []))) 68 | 69 | (testing "With backslash" 70 | (is (= (comparable-group-spec-for "/some\\\\path => G1") 71 | (comparable-group-spec [{:path "^/some\\\\path/" 72 | :name "G1"}])))) 73 | 74 | (testing "With dot in filename" 75 | (is (= (comparable-group-spec-for "/some/path/with.dot => G1") 76 | (comparable-group-spec [{:path "^/some/path/with.dot/" 77 | :name "G1"}])))) 78 | 79 | (testing "With dash in filename" 80 | (is (= (comparable-group-spec-for "/some/path/with-dash/x => G1") 81 | (comparable-group-spec [{:path "^/some/path/with-dash/x/" 82 | :name "G1"}]))))) 83 | 84 | (def ^:const entities-in-same-layer [{:entity "Top/A" :rev 1} 85 | {:entity "Top/B" :rev 2}]) 86 | 87 | (def ^:const entities-multiple-layers [{:entity "Top/A" :rev 1} 88 | {:entity "Bottom/B" :rev 2}]) 89 | 90 | (def ^:const entities-in-layers [{:entity "Layer/A/Entity" :rev 1} 91 | {:entity "Layer/B/Entity" :rev 2}]) 92 | 93 | (def as-ds incanter/to-dataset) 94 | 95 | ;; Specify the path to match a physical layer together with the name 96 | ;; of the logical layer: 97 | (def ^:const top-level-layer [{:path #"^Top/" :name "T"}]) 98 | (def ^:const multiple-layers [{:path #"^Top/" :name "Top"} {:path #"^Bottom/" :name "infrastructure"}]) 99 | (def ^:const regex-one-layer [{:path #"^.*/A/.*$" :name "A Entities"}]) 100 | (def ^:const regex-same-layers [{:path #"^.*/Entity$" :name "All Entities"}]) 101 | (def ^:const regex-multiple-layers 102 | [{:path #"^.*/A/.*$" :name "A Entities"} 103 | {:path #"^.*/B/.*$" :name "B Entities"}]) 104 | 105 | (deftest entities-are-mapped-to-defined-layers 106 | (testing "Mapped to the same layer" 107 | (is (= (g/map-entities->groups entities-in-same-layer top-level-layer) 108 | [{:rev 1 :entity "T"} 109 | {:rev 2 :entity "T"}]))) 110 | 111 | (testing "Mapped to different layers" 112 | (is (= (g/map-entities->groups entities-multiple-layers multiple-layers) 113 | [{:rev 1 :entity "Top"} 114 | {:rev 2 :entity "infrastructure"}]))) 115 | 116 | (testing "Mapped via regex to the same layer" 117 | (is (= (g/map-entities->groups entities-in-layers regex-same-layers) 118 | [{:rev 1 :entity "All Entities"} 119 | {:rev 2 :entity "All Entities"}]))) 120 | 121 | (testing "Mapped via regex to different layers" 122 | (is (= (g/map-entities->groups entities-in-layers regex-multiple-layers) 123 | [{:rev 1 :entity "A Entities"} 124 | {:rev 2 :entity "B Entities"}])))) 125 | 126 | ;; Filter out any entity that doesn't match the given layer structure. 127 | ;; Most of the time this is probably what we want. 128 | (deftest unmapped-entities-are-ignored 129 | (is (= (g/map-entities->groups entities-multiple-layers top-level-layer) 130 | [{:rev 1 :entity "T"}])) 131 | (is (= (g/map-entities->groups entities-in-layers regex-one-layer) 132 | [{:rev 1 :entity "A Entities"}]))) 133 | -------------------------------------------------------------------------------- /test/code_maat/app/team_mapper_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2017 Adam Tornhill 2 | ;;; 3 | 4 | (ns code-maat.app.team-mapper-test 5 | (:require [code-maat.app.team-mapper :as m]) 6 | (:use [clojure.test])) 7 | 8 | (def ^:private commits [{:entity "A" :rev 1 :author "X"} 9 | {:entity "B" :rev 2 :author "Me Myself"} 10 | {:entity "A" :rev 3 :author "X"} 11 | {:entity "C" :rev 17 :author "Someone Else"}]) 12 | 13 | (deftest translates-authors-to-teams 14 | (testing "Maps all authors to the same team" 15 | (is (= [{:entity "A" :rev 1 :author "A Team"} 16 | {:entity "B" :rev 2 :author "A Team"} 17 | {:entity "A" :rev 3 :author "A Team"} 18 | {:entity "C" :rev 17 :author "A Team"}] 19 | (m/run commits {"X" "A Team" 20 | "Me Myself" "A Team" 21 | "Someone Else" "A Team"})))) 22 | (testing "Maps the authors to different teams" 23 | (is (= [{:entity "A" :rev 1 :author "C Team"} 24 | {:entity "B" :rev 2 :author "B Team"} 25 | {:entity "A" :rev 3 :author "C Team"} 26 | {:entity "C" :rev 17 :author "A Team"}] 27 | (m/run commits {"X" "C Team" 28 | "Me Myself" "B Team" 29 | "Someone Else" "A Team"})))) 30 | (testing "Unmapped authors are kept as-is" 31 | (is (= [{:entity "A" :rev 1 :author "X"} ; no mapping 32 | {:entity "B" :rev 2 :author "B Team"} 33 | {:entity "A" :rev 3 :author "X"} 34 | {:entity "C" :rev 17 :author "A Team"}] ; no mapping 35 | (m/run commits {"Me Myself" "B Team" 36 | "Someone Else" "A Team"}))))) 37 | -------------------------------------------------------------------------------- /test/code_maat/app/time_based_end_to_end_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2014 Adam Tornhill 2 | ;;; 3 | 4 | (ns code-maat.app.time-based-end-to-end-test 5 | (:require [code-maat.app.app :as app]) 6 | (:use [clojure.test] 7 | [code-maat.tools.test-tools])) 8 | 9 | ;;; End-to-end tests to simulate a time-based analysis. 10 | ;;; 11 | ;;; The test data contains two commits done the same day. 12 | ;;; With the default options we'll treat them as separate. 13 | ;;; In a time-base analysis we consider them as a logical 14 | ;;; part of the same work. 15 | 16 | (def ^:const log-file "./test/code_maat/app/day_coupled_entities_git.txt") 17 | 18 | (def ^:const csv-options 19 | {:version-control "git" 20 | :analysis "coupling" 21 | :min-revs 1 22 | :min-shared-revs 1 23 | :min-coupling 10 24 | :max-coupling 100 25 | :max-changeset-size 10}) 26 | 27 | (def ^:const csv-options-for-time-based 28 | (merge csv-options {:temporal-period "1"})) 29 | 30 | (deftest only-calculates-coupling-within-same-commit-by-default 31 | (is (= (run-with-str-output log-file csv-options) 32 | "entity,coupled,degree,average-revs\n/Infrastrucure/Network/Connection.cs,/Presentation/Status/ClientPresenter.cs,100,1\n"))) 33 | 34 | (deftest calculates-coupling-within-same-day 35 | (is (= (run-with-str-output log-file csv-options-for-time-based) 36 | "entity,coupled,degree,average-revs\n/Infrastrucure/Network/Connection.cs,/Presentation/Status/ClientPresenter.cs,100,1\n/Infrastrucure/Network/Connection.cs,/Infrastrucure/Network/TcpConnection.cs,100,1\n/Infrastrucure/Network/TcpConnection.cs,/Presentation/Status/ClientPresenter.cs,100,1\n"))) 37 | 38 | (def ^:const options-with-invalid-time-period 39 | (merge csv-options {:temporal-period "not a number"})) 40 | 41 | (deftest throws-on-unsupported-time-periods 42 | "We hope to support more options in the future." 43 | (is (thrown? IllegalArgumentException 44 | (run-with-str-output log-file options-with-invalid-time-period)))) 45 | -------------------------------------------------------------------------------- /test/code_maat/app/time_based_grouper_test.clj: -------------------------------------------------------------------------------- 1 | (ns code-maat.app.time-based-grouper-test 2 | (:require [code-maat.app.time-based-grouper :as grouper]) 3 | (:use [clojure.test])) 4 | 5 | (deftest commits-by-day 6 | (testing "Expect a non-modifying operation" 7 | (let [input-commits [{:entity "A" :rev 1 :date "2022-10-20"} 8 | {:entity "B" :rev 2 :date "2022-10-20"}]] 9 | (is (= [{:date "2022-10-20" :entity "A" :rev "2022-10-20"} 10 | {:date "2022-10-20" :entity "B" :rev "2022-10-20"}] 11 | (grouper/by-time-period input-commits {:temporal-period "1"})))))) 12 | 13 | (deftest multiple-days-give-a-rolling-dataset 14 | (let [input-commits [{:entity "A" :rev 1 :date "2022-10-20"} 15 | {:entity "B" :rev 2 :date "2022-10-20"} 16 | 17 | {:entity "B" :rev 3 :date "2022-10-19"} ; double entry, two B's when looking at last two days 18 | {:entity "D" :rev 3 :date "2022-10-19"} 19 | 20 | {:entity "C" :rev 4 :date "2022-10-18"} 21 | {:entity "D" :rev 4 :date "2022-10-18"} 22 | 23 | {:entity "D" :rev 5 :date "2022-10-15"}]] ; a gap in days between the commits 24 | (is (= [ 25 | ; Only commits on 2022-10-15, not on subsequent day: 26 | {:date "2022-10-15" :entity "D" :rev "2022-10-15"} 27 | 28 | ; 17-18th 29 | {:date "2022-10-18" :entity "C" :rev "2022-10-18"} 30 | {:date "2022-10-18" :entity "D" :rev "2022-10-18"} 31 | 32 | ; 18-19th 33 | {:date "2022-10-18" :entity "C" :rev "2022-10-19"} 34 | {:date "2022-10-18" :entity "D" :rev "2022-10-19"} 35 | {:date "2022-10-19" :entity "B" :rev "2022-10-19"} 36 | 37 | ; 19-20th 38 | {:date "2022-10-19" :entity "B" :rev "2022-10-20"} 39 | {:date "2022-10-19" :entity "D" :rev "2022-10-20"} 40 | {:date "2022-10-20" :entity "A" :rev "2022-10-20"}] 41 | (grouper/by-time-period input-commits {:temporal-period "2"}))))) 42 | 43 | (deftest edge-cases 44 | (testing "Works on an empty input sequence, ie. no commits" 45 | (is (= [] 46 | (grouper/by-time-period [] {:temporal-period "2"})))) 47 | (testing "Works on a single commit" 48 | (is (= [{:date "2022-10-19" :entity "B" :rev "2022-10-19"}] 49 | (grouper/by-time-period [{:entity "B" :rev 3 :date "2022-10-19"}] {:temporal-period "1"}))))) 50 | -------------------------------------------------------------------------------- /test/code_maat/dataset/dataset_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.dataset.dataset-test 7 | (:require [code-maat.dataset.dataset :as ds] 8 | [incanter.core :as incanter] 9 | [code-maat.analysis.test-data :as test-data]) 10 | (:use clojure.test)) 11 | 12 | (deftest recognizes-empty-dataset 13 | (is (ds/-empty? test-data/empty-vcsd)) 14 | (is (not (ds/-empty? test-data/vcsd)))) 15 | 16 | (deftest groups-by-given-column 17 | (let [group (ds/-group-by :entity test-data/vcsd)] 18 | (is (= (keys group) 19 | [{:entity "A"} {:entity "B"}])))) 20 | 21 | (deftest selects-by-given-column 22 | (testing "Multiple rows in dataset" 23 | (is (= (ds/-select-by :entity test-data/vcsd) 24 | ["A" "B" "A" "A"]))) 25 | (testing "Single row dataset" 26 | (is (= (ds/-select-by :entity test-data/single-vcsd) 27 | ["A"]))) 28 | (testing "No rows" 29 | (is (= (ds/-select-by :entity test-data/empty-vcsd) 30 | [])))) 31 | 32 | (deftest counts-rows 33 | (is (= (ds/-nrows test-data/vcsd) 34 | 4))) 35 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/churn_scenario_test.clj: -------------------------------------------------------------------------------- 1 | (ns code-maat.end-to-end.churn-scenario-test 2 | (:require [code-maat.app.app :as app] 3 | [code-maat.analysis.test-data :as test-data] 4 | [code-maat.test.data-driven :as dd]) 5 | (:use clojure.test)) 6 | 7 | ;;; This module contains end-to-end tests running the whole app 8 | ;;; from front-end to back-end with respect to code churn. 9 | 10 | (def ^:const git-log-file "./test/code_maat/end_to_end/simple_git.txt") 11 | 12 | (defn- run-with-str-output [log-file options] 13 | (with-out-str 14 | (app/run log-file options))) 15 | 16 | (deftest calculates-absolute-churn 17 | (is (= (run-with-str-output git-log-file 18 | {:version-control "git" 19 | :analysis "abs-churn"}) 20 | "date,added,deleted,commits\n2013-02-07,18,2,1\n2013-02-08,4,6,1\n"))) 21 | 22 | (deftest calculates-churn-by-author 23 | "The total churn of each individual contributor" 24 | (is (= (run-with-str-output git-log-file 25 | {:version-control "git" 26 | :analysis "author-churn"}) 27 | "author,added,deleted,commits\nAPT,4,6,1\nXYZ,18,2,1\n"))) 28 | 29 | (deftest calculates-churn-by-entity 30 | "Identify entities with the highest churn rate." 31 | (is (= (run-with-str-output git-log-file 32 | {:version-control "git" 33 | :analysis "entity-churn"}) 34 | "entity,added,deleted,commits\n/Infrastrucure/Network/Connection.cs,19,4,2\n/Presentation/Status/ClientPresenter.cs,3,4,1\n"))) 35 | 36 | (deftest calculates-ownership-by-churn 37 | "Calculate amount of individual contributions based on the 38 | churn of each author on a per entity-basis." 39 | (is (= (run-with-str-output git-log-file 40 | {:version-control "git" 41 | :analysis "entity-ownership"}) 42 | "entity,author,added,deleted\n/Infrastrucure/Network/Connection.cs,APT,1,2\n/Infrastrucure/Network/Connection.cs,XYZ,18,2\n/Presentation/Status/ClientPresenter.cs,APT,3,4\n"))) 43 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/empty.git: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/test/code_maat/end_to_end/empty.git -------------------------------------------------------------------------------- /test/code_maat/end_to_end/empty.hg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/test/code_maat/end_to_end/empty.hg -------------------------------------------------------------------------------- /test/code_maat/end_to_end/empty.p4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamtornhill/code-maat/e745abece16b47adbb18d63fdaba39eb31c69204/test/code_maat/end_to_end/empty.p4 -------------------------------------------------------------------------------- /test/code_maat/end_to_end/empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/git2_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.git2-live-data-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const git-log-file "./test/code_maat/end_to_end/roslyn_git.log") 11 | 12 | (deftest parses-live-data 13 | (is (= (with-out-str 14 | (app/run git-log-file 15 | {:version-control "git2" 16 | :analysis "revisions" 17 | :rows 2})) 18 | "entity,n-revs\nsrc/Features/Core/EditAndContinue/ActiveStatementFlags.cs,1\nsrc/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs,1\n"))) 19 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/git2_live_data_test_with_group.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.git2-live-data-test-with-group 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const git-log-file "./test/code_maat/end_to_end/roslyn_git.log") 11 | (def ^:const text-group-file "./test/code_maat/end_to_end/text-layers-definition.txt") 12 | (def ^:const regex-group-file "./test/code_maat/end_to_end/regex-layers-definition.txt") 13 | (def ^:const regex-and-text-group-file "./test/code_maat/end_to_end/regex-and-text-layers-definition.txt") 14 | 15 | (defn- join-lines 16 | [lines] 17 | (clojure.string/join "\n" (conj lines ""))) 18 | 19 | (deftest parses-live-data-with-text-groups 20 | (is (= (with-out-str 21 | (app/run git-log-file 22 | {:version-control "git2" 23 | :analysis "revisions" 24 | :group text-group-file 25 | })) 26 | (join-lines 27 | ["entity,n-revs" 28 | "Interactive Layer,3" 29 | "Editor Layer,3" 30 | ])))) 31 | 32 | (deftest parses-live-data-with-regex-groups 33 | (is (= (with-out-str 34 | (app/run git-log-file 35 | {:version-control "git2" 36 | :analysis "revisions" 37 | :group regex-group-file 38 | })) 39 | (join-lines 40 | ["entity,n-revs" 41 | "Code,7" 42 | "Unit Tests,4" 43 | ])))) 44 | 45 | (deftest parses-live-data-with-regex-and-text-groups 46 | (is (= (with-out-str 47 | (app/run git-log-file 48 | {:version-control "git2" 49 | :analysis "revisions" 50 | :group regex-and-text-group-file 51 | })) 52 | (join-lines 53 | ["entity,n-revs" 54 | "Core,4" 55 | "CS Tests,2" 56 | "Images,1" 57 | "VB Tests,1" 58 | ])))) 59 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/git_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.git-live-data-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const git-log-file "./test/code_maat/end_to_end/mono_git.log") 11 | 12 | (deftest parses-live-data 13 | (is (= (with-out-str 14 | (app/run git-log-file 15 | {:version-control "git" 16 | :analysis "authors" 17 | :rows 2})) 18 | "entity,n-authors,n-revs\nmono/mini/method-to-ir.c,2,5\nmcs/class/corlib/System/Console.iOS.cs,2,3\n"))) 19 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/mercurial_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.mercurial-live-data-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const hg-log-file "./test/code_maat/end_to_end/tpp_hg.log") 11 | 12 | (deftest parses-live-data 13 | (is (= (with-out-str 14 | (app/run hg-log-file 15 | {:version-control "hg" 16 | :analysis "authors" 17 | :rows 2})) 18 | "entity,n-authors,n-revs\ntest/CMakeLists.txt,3,21\nCMakeLists.txt,3,6\n"))) 19 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/mono_git.log: -------------------------------------------------------------------------------- 1 | [9b5bc71] Lluis Sanchez 2013-08-29 Fixed bug #14295 - Project.Load incorrectly resets the FullFileName property 2 | 4 2 mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs 3 | 11 0 mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs 4 | 5 | [18baa67] Rodrigo Kumpera 2013-08-28 Fix some warning. 6 | 6 6 mono/mini/method-to-ir.c 7 | 8 | [a9b4ca9] Rodrigo Kumpera 2013-08-28 [runtime]Don't assume MonoType extends directly from System.Type 9 | 1 1 mono/metadata/marshal.c 10 | 1 1 mono/mini/method-to-ir.c 11 | 12 | [fc522a4] Marek Safar 2013-08-28 Fixes parameter name 13 | 7 6 mcs/class/corlib/System.Collections.ObjectModel/Collection.cs 14 | 15 | [2f639b3] Marek Safar 2013-08-28 Use IReflectType interface instead of TypeDelegator for introspection type extensions. Fixes #14168 16 | 20 1 mcs/class/corlib/System.Reflection.Emit/EnumBuilder.cs 17 | 6 1 mcs/class/corlib/System.Reflection.Emit/GenericTypeParameterBuilder.cs 18 | 18 1 mcs/class/corlib/System.Reflection.Emit/TypeBuilder.cs 19 | 7 2 mcs/class/corlib/System.Reflection/IntrospectionExtensions.cs 20 | 6 1 mcs/class/corlib/System.Reflection/MonoGenericClass.cs 21 | 14 0 mcs/class/corlib/System.Reflection/TypeDelegator.cs 22 | 5 24 mcs/class/corlib/System.Reflection/TypeInfo.cs 23 | 7 1 mcs/class/corlib/System/MonoType.cs 24 | 57 0 mcs/class/corlib/Test/System.Reflection/IntrospectionExtensionsTest.cs 25 | 1 0 mcs/class/corlib/corlib_test.dll.sources 26 | 27 | [af31330] Marek Safar 2013-08-28 Make setProperties private 28 | 1 1 mcs/class/System.Drawing/System.Drawing/Font.cs 29 | 30 | [abe9522] Marek Safar 2013-08-28 Merge pull request #743 from gvillanueva/master 31 | [234c346] gvillanueva 2013-08-27 Rewrote Font.GetHashCode() to improve uniqueness. Added test cases to prevent reintroduction of collision scenarios observed in Font.GetHashCode(). 32 | 16 2 mcs/class/System.Drawing/System.Drawing/Font.cs 33 | 30 0 mcs/class/System.Drawing/Test/System.Drawing/TestFont.cs 34 | 35 | [be400d4] Rodrigo Kumpera 2013-08-27 Naming cleanup. 36 | 3 3 mcs/class/corlib/System/Console.iOS.cs 37 | 38 | [b08d9c2] Miguel de Icaza 2013-08-27 Console.iOS: Reset sb.Length in all cases, even for poor man's print 39 | 1 1 mcs/class/corlib/System/Console.iOS.cs 40 | 41 | [982f710] Marek Safar 2013-08-27 Invalid syntax during attribute target parsing can crash parser. Fixes #14245 42 | 18 0 mcs/errors/cs0658-4.cs 43 | 1 1 mcs/mcs/attribute.cs 44 | 45 | [fd12f0b] Miguel de Icaza 2013-08-26 Console.iOS: add fallback for the case where monotouch_log is not linked in 46 | 17 3 mcs/class/corlib/System/Console.iOS.cs 47 | 48 | [b2cc26a] Michael Hutchinson 2013-08-26 Merge pull request #742 from remobjects/import-project-userfile 49 | [60fd58a] Carlo Kok 2013-08-26 Import ".user" as if it was part of the project, this lets users override the options without checking changes to the main project file. 50 | 2 0 mcs/tools/xbuild/xbuild/2.0/Microsoft.Common.targets 51 | 2 0 mcs/tools/xbuild/xbuild/3.5/Microsoft.Common.targets 52 | 2 0 mcs/tools/xbuild/xbuild/4.0/Microsoft.Common.targets 53 | 54 | [4ec5963] Marek Habersack 2013-08-26 Merge pull request #726 from pruiz/xamarin-bug-13708 55 | [8e5e7ea] Marek Safar 2013-08-26 Merge pull request #741 from marcominetti/master 56 | [94e8ad6] marcominetti 2013-08-26 cleaned console logs in MakeExpression function 57 | 0 2 mcs/mcs/nullable.cs 58 | 59 | [cf0b312] marcominetti 2013-08-26 cleaned console logs in MakeExpression function 60 | 0 2 mcs/mcs/expression.cs 61 | 62 | [16013bc] Marek Safar 2013-08-26 Merge pull request #740 from pruiz/dataannotations-enhancements 63 | [71d6896] Pablo Ruiz Garcia 2013-08-26 Reverted string.Format(CultureInfo.CurrentCulture, ..) change. 64 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/CompareAttribute.cs 65 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/FileExtensionsAttribute.cs 66 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/MaxLengthAttribute.cs 67 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/MinLengthAttribute.cs 68 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/RangeAttribute.cs 69 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/RegularExpressionAttribute.cs 70 | 2 2 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/StringLengthAttribute.cs 71 | 1 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/ValidationAttribute.cs 72 | 73 | [8c2c29d] Marek Safar 2013-08-26 Add test for #12745 74 | 12 0 mcs/class/corlib/Test/System.Runtime.CompilerServices/TaskAwaiterTest.cs 75 | 76 | [46e571e] Pablo Ruiz Garcia 2013-08-25 Set correct author on test files. 77 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/CompareAttributeTest.cs 78 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/CreditCardAttributeTest.cs 79 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/EmailAddressAttributeTest.cs 80 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/FileExtensionsAttributeTest.cs 81 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/PhoneAttributeTest.cs 82 | 83 | [eb25dfc] Pablo Ruiz Garcia 2013-08-25 Some enhancements and additions to DataAnnotations, incluiding implemented of a few missing pieces of Sys.ComponentModel.DataAnnotations v4.5, like: - CompareAttribute - CreditCardAttribute - EmailAddressAttribute - FileExtensionsAttribute - PhoneAttribute - Implemented IsValid() methods of Min/MaxLengthAttribute. - Fixed all existing Validation attributes so they take into account current's locale when composing ErrorMessage. 84 | 118 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/CompareAttribute.cs 85 | 81 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/CreditCardAttribute.cs 86 | 6 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/DataType.cs 87 | 23 2 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/DataTypeAttribute.cs 88 | 73 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/EmailAddressAttribute.cs 89 | 92 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/FileExtensionsAttribute.cs 90 | 43 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/MaxLengthAttribute.cs 91 | 34 9 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/MinLengthAttribute.cs 92 | 69 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/PhoneAttribute.cs 93 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/RangeAttribute.cs 94 | 2 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/RegularExpressionAttribute.cs 95 | 3 2 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/StringLengthAttribute.cs 96 | 8 4 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/TimestampAttribute.cs 97 | 8 1 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/ValidationAttribute.cs 98 | 11 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations/ValidationResult.cs 99 | 5 0 mcs/class/System.ComponentModel.DataAnnotations/System.ComponentModel.DataAnnotations_test.dll.sources 100 | 69 0 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/CompareAttributeTest.cs 101 | 59 0 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/CreditCardAttributeTest.cs 102 | 57 0 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/EmailAddressAttributeTest.cs 103 | 60 0 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/FileExtensionsAttributeTest.cs 104 | 57 0 mcs/class/System.ComponentModel.DataAnnotations/Test/System.ComponentModel.DataAnnotations/PhoneAttributeTest.cs 105 | 5 0 mcs/class/System.ComponentModel.DataAnnotations/net_4_5_System.ComponentModel.DataAnnotations.dll.sources 106 | 107 | [0d864b0] Sebastien Pouliot 2013-08-24 SRE.ParameterBuilder cannot be exposed in FULL_AOT_RUNTIME 108 | 2 1 mcs/class/corlib/System.Reflection/ParameterInfo.cs 109 | 110 | [3b846d3] Rodrigo Kumpera 2013-08-23 We have g_getenv for portability reasons, so let's use it. 111 | 1 1 eglib/src/gfile-posix.c 112 | 1 1 mono/io-layer/shared.c 113 | 3 3 mono/metadata/boehm-gc.c 114 | 1 1 mono/metadata/cominterop.c 115 | 2 2 mono/metadata/console-unix.c 116 | 1 1 mono/metadata/lock-tracer.c 117 | 2 2 mono/metadata/sgen-gc.c 118 | 1 1 mono/mini/debugger-agent.c 119 | 3 3 mono/mini/driver.c 120 | 2 2 mono/mini/ir-emit.h 121 | 1 1 mono/mini/liveness.c 122 | 1 1 mono/mini/main.c 123 | 4 4 mono/mini/method-to-ir.c 124 | 1 1 mono/mini/mini-arm.c 125 | 6 6 mono/mini/mini-gc.c 126 | 1 1 mono/mini/mini-llvm-cpp.cpp 127 | 3 3 mono/mini/mini-llvm.c 128 | 1 1 mono/mini/mini-x86.c 129 | 18 18 mono/mini/mini.c 130 | 2 2 mono/mini/ssapre.c 131 | 1 1 mono/utils/mono-hwcap.c 132 | 2 2 mono/utils/mono-logger.c 133 | 134 | [722ace9] Zoltan Varga 2013-08-23 Fix yet another DISABLE_JIT bug in wrapper generation. Fixes #14194. 135 | 10 0 mono/metadata/marshal.c 136 | 137 | [20d08fd] Marek Safar 2013-08-23 Annonymous method storey inside async storey has to capture this directly when the storey does not have any parents. Fixes #14126 138 | 1 1 mcs/mcs/anonymous.cs 139 | 25 4 mcs/mcs/statement.cs 140 | 45 0 mcs/tests/test-async-49.cs 141 | 51 0 mcs/tests/ver-il-net_4_5.xml 142 | 143 | [29c42bf] Rodrigo Kumpera 2013-08-22 Add tests for the new 4.0 behavior in ParameterInfo. 144 | 164 0 mcs/class/corlib/Test/System.Reflection/ParameterInfoTest.cs 145 | 146 | [3673d05] Rodrigo Kumpera 2013-08-22 [corlib]Implement .net 4.0 ParameterInfo. 147 | 1 1 mcs/class/corlib/System.Reflection.Emit/ConstructorBuilder.cs 148 | 2 2 mcs/class/corlib/System.Reflection.Emit/ConstructorOnTypeBuilderInst.cs 149 | 1 1 mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs 150 | 1 1 mcs/class/corlib/System.Reflection.Emit/MethodBuilder.cs 151 | 1 1 mcs/class/corlib/System.Reflection/MonoMethod.cs 152 | 240 0 mcs/class/corlib/System.Reflection/MonoParameterInfo.cs 153 | 1 1 mcs/class/corlib/System.Reflection/MonoProperty.cs 154 | 85 131 mcs/class/corlib/System.Reflection/ParameterInfo.cs 155 | 1 0 mcs/class/corlib/corlib.dll.sources 156 | 7 3 mono/metadata/reflection.c 157 | 158 | [cfa7bad] Marek Safar 2013-08-22 Fixes negative symbol definition for few locales to be simple -. Fixes #14185 159 | 974 976 mono/metadata/culture-info-tables.h 160 | 8 2 tools/locale-builder/Driver.cs 161 | 162 | [33bd250] Zoltan Varga 2013-08-22 Disable two delegate tests on mobile. 163 | 6 0 mcs/class/corlib/Test/System/DelegateTest.cs 164 | 165 | [95a6979] Marek Safar 2013-08-22 Enable fixed failing test 166 | 0 1 mcs/tests/known-issues-net_4_5 167 | 16 0 mcs/tests/ver-il-net_4_5.xml 168 | 169 | [9bcd33f] Neale Ferguson 2013-08-22 Enable INLINE support for s390x 170 | 3 0 mono/metadata/mono-debug.h 171 | 8 0 mono/mini/debug-mini.c 172 | 14 54 mono/mini/mini-s390x.c 173 | 174 | [b46b038] Sebastien Pouliot 2013-08-21 Fix test case not to assume it can write in the current directory 175 | 3 2 mcs/class/System.XML/Test/System.Xml.Xsl/XslTransformTests.cs 176 | 177 | [7770413] Mark Probst 2013-08-21 [sgen] Disable cementing for parallel mark&sweep. 178 | 8 2 mono/metadata/sgen-gc.c 179 | 180 | [f2e7f36] Sebastien Pouliot 2013-08-21 Use the invarient culture calendar if the requested one is not available. Partial fix for #13977 181 | 1 1 mcs/class/corlib/System.Globalization/CultureInfo.cs 182 | 183 | [41290d3] Atsushi Eno 2013-08-22 Fix bug #14143 - xsl:import in included stylesheet caused compilation error. 184 | 3 1 mcs/class/System.XML/Mono.Xml.Xsl/XslStylesheet.cs 185 | 186 | [a700784] Rodrigo Kumpera 2013-08-21 [runtime] Don't allow execution of dynamic assemblies without run access. Fixes #7126 187 | 26 2 mcs/class/corlib/Test/System.Reflection.Emit/AssemblyBuilderTest.cs 188 | 8 1 mono/metadata/icall.c 189 | 190 | [de2efdb] Rodrigo Kumpera 2013-08-21 [corlib]Fix 2.0 test suite breakage. 191 | 1 1 mcs/class/corlib/Test/System/TypeTest.cs 192 | 193 | [d71ca46] Rodrigo Kumpera 2013-08-21 [verifier]Remove duplicate check. 194 | 0 2 mono/metadata/verify.c 195 | 196 | [2d6335f] Atsushi Eno 2013-08-21 revert default serialization: ignore non-public members. Also need to exclude backing field in non-default serialization. 197 | 8 4 mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/TypeMap.cs 198 | 199 | [bfff327] Rodrigo Kumpera 2013-08-20 [runtime]Add some error checking to custom attr parsing and plug a memory leak. Fixes #13435. 200 | 28 2 mono/metadata/reflection.c 201 | 2 1 mono/mini/debugger-agent.c 202 | 203 | [8cb8add] Zoltan Varga 2013-08-21 Make the dmcs/gmcs scripts identical to the mcs script with an extra -sdk: argument, to avoid PATH problems, and to allow the windows installer to generate .bat files for them. Hopefully fixes #8813. 204 | 1 1 scripts/dmcs.in 205 | 1 1 scripts/gmcs.in 206 | 207 | [e0c4e5b] Rodrigo Kumpera 2013-08-20 [runtime] Handle custom attributes with nested array of enums. Fixes #13767 208 | 41 0 mcs/class/corlib/Test/System/TypeTest.cs 209 | 19 0 mono/metadata/reflection.c 210 | 211 | [0152cee9] Zoltan Varga 2013-08-21 Use i686-pc-mingw32-gcc as the mkbundle compiler on windows. 212 | 1 1 mcs/tools/mkbundle/mkbundle.cs 213 | 214 | [7685ca6] Zoltan Varga 2013-08-20 Merge pull request #737 from Mixaill/master 215 | [ac146cb] Rodrigo Kumpera 2013-08-20 [verify]Relax the restriction on global methods visibility. Fixes #13890. 216 | 2 2 mono/metadata/metadata-verify.c 217 | 218 | [c187e5e] Rodrigo Kumpera 2013-08-20 [verifier]Since 4.5 arrays now implement IReadOnlyList and IReadOnlyCollection. Verify this properly. Fixes #13879. 219 | 28 0 mono/metadata/verify.c 220 | 221 | [ba8d128] Zoltan Varga 2013-08-20 Add support for more return types for constrained gsharedvt calls. Fixes #13030. 222 | 0 2 mono/mini/aot-compiler.c 223 | 28 1 mono/mini/gshared.cs 224 | 7 10 mono/mini/method-to-ir.c 225 | 2 0 mono/mini/mini.h 226 | 227 | [c686f20] Zoltan Varga 2013-08-20 Enable support for constrained gsharedvt calls to Object.GetType (). 228 | 20 0 mono/mini/gshared.cs 229 | 22 11 mono/mini/jit-icalls.c 230 | 4 5 mono/mini/method-to-ir.c 231 | 232 | [74fc821] Zoltan Varga 2013-08-20 Fix a regression introduced by d84a165c8e799d8734df5f51a6dcf12b1f2a3695. Unbox casts were failing for enums. Fixes #14026. 233 | 6 1 mono/mini/gshared.cs 234 | 3 5 mono/mini/jit-icalls.c 235 | 236 | [e50f956] Paolo Molaro 2013-08-20 Merge pull request #739 from mono/upstream-master 237 | [25961ac] Paolo Molaro 2013-08-20 Ported perf code to work on arm and adjusted to changes in the kernel that broke mmap(). 238 | 143 39 mono/profiler/proflog.c 239 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/mono_git_team_map.csv: -------------------------------------------------------------------------------- 1 | author,team 2 | Lluis Sanchez,Mono 3 | Rodrigo Kumpera,Another Team -------------------------------------------------------------------------------- /test/code_maat/end_to_end/perforce_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Robert Creager 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.perforce-live-data-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const p4-log-file "./test/code_maat/end_to_end/sample_p4.log") 11 | 12 | (deftest parses-live-data 13 | (is (= (with-out-str 14 | (app/run p4-log-file 15 | {:version-control "p4" 16 | :analysis "authors" 17 | :rows 2})) 18 | "entity,n-authors,n-revs\n/src/something/else/mineral/SweepCmd.cpp,1,1\n/src/something/else/animal/Init.cpp,1,1\n"))) 19 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/regex-and-text-layers-definition.txt: -------------------------------------------------------------------------------- 1 | src/Features/Core => Core 2 | ^src\/.*\/.*Tests\.cs$ => CS Tests 3 | ^src\/.*\/.*Tests\.vb$ => VB Tests 4 | ^src\/.*\.png$ => Images 5 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/regex-layers-definition.txt: -------------------------------------------------------------------------------- 1 | ^src\/((?!.*Test.*).).*$ => Code 2 | ^src\/.*Test.*$ => Unit Tests 3 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/roslyn_git.log: -------------------------------------------------------------------------------- 1 | --8648e6c--2015-06-04--Jonathon Marolf 2 | --0d20709--2015-06-04--Dustin Campbell 3 | --25f1cf0--2015-06-04--Dustin Campbell 4 | --3634956--2015-06-04--Charles Stoner 5 | --b770795--2015-06-04--Manish Vasani 6 | --6bdb9d6--2015-06-04--AlekseyTs 7 | --f40efcf--2015-06-04--Tomáš Matoušek 8 | --dec9863--2015-06-04--Evan Hauck 9 | --9df62c6--2015-06-04--Charles Stoner 10 | 34 43 src/InteractiveWindow/VisualStudio/InteractiveWindow.vsct 11 | - - src/InteractiveWindow/VisualStudio/Resources/InteractiveToolbarImages.png 12 | 0 1 src/InteractiveWindow/VisualStudio/VisualStudioInteractiveWindow.csproj 13 | 14 | --9bac786--2015-06-04--Evan Hauck 15 | 2 2 src/Tools/Source/RunTests/TestRunner.cs 16 | 17 | --b643b08--2015-06-04--Charles Stoner 18 | --044c169--2015-06-04--Tomas Matousek 19 | 39 0 src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs 20 | 1 0 src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs 21 | 36 0 src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb 22 | 3 2 src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs 23 | 7 0 src/Features/Core/EditAndContinue/ActiveStatementFlags.cs 24 | 2 0 src/Features/Core/EditAndContinue/RudeEditDiagnosticDescriptors.cs 25 | 1 0 src/Features/Core/EditAndContinue/RudeEditKind.cs 26 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/sample_p4.log: -------------------------------------------------------------------------------- 1 | Change 1113831 by user@client on 2015/01/20 16:15:09 2 | HM-2797 Some comment +review HMR 3 | Affected files ... 4 | ... //depot/stream/src/something/else/Command.cpp#9 edit 5 | ... //depot/stream/src/something/else/animal/Fwd.hpp#15 edit 6 | ... //depot/stream/src/something/else/mineral/BeginCmd.cpp#9 edit 7 | ... //depot/stream/src/something/else/mineral/Xml.cpp#29 edit 8 | ... //depot/stream/src/something/else/mineral/Xml.hpp#25 edit 9 | ... //depot/stream/test/something/else/mineral/Test_Begin.cpp#9 edit 10 | 11 | Change 1113724 by user@client on 2015/01/20 10:37:58 12 | HM-2728 white space +review HMR-319 13 | Affected files ... 14 | ... //depot/stream/src/something/else/mineral/SweepCmd.cpp#13 edit 15 | ... //depot/stream/src/something/else/mineral/SweepCmd.hpp#9 edit 16 | 17 | Change 1113715 by user@client on 2015/01/20 10:07:03 18 | HM-2728 Removed unnecessary include 19 | Affected files ... 20 | ... //depot/stream/src/something/else/animal/Init.cpp#44 edit 21 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/scenario_tests.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.scenario-tests 7 | (:require [code-maat.app.app :as app] 8 | [code-maat.analysis.test-data :as test-data] 9 | [code-maat.test.data-driven :as dd]) 10 | (:use [clojure.test] 11 | [code-maat.tools.test-tools])) 12 | 13 | ;;; This module contains end-to-end tests running the whole app 14 | ;;; from fron-end to back-end. 15 | ;;; Most test cases are data-driven; I parameterize them with 16 | ;;; the options (i.e. specify a VCS) and a log file for that 17 | ;;; particular option. This strategy is based on the content 18 | ;;; of the specified log files below. The log files contain 19 | ;;; the same information, just on different VCS formats. 20 | 21 | (def ^:const svn-log-file "./test/code_maat/end_to_end/simple.xml") 22 | (def ^:const git-log-file "./test/code_maat/end_to_end/simple_git.txt") 23 | (def ^:const git2-log-file "./test/code_maat/end_to_end/simple_git2.txt") 24 | (def ^:const hg-log-file "./test/code_maat/end_to_end/simple_hg.txt") 25 | (def ^:const p4-log-file "./test/code_maat/end_to_end/simple_p4.txt") 26 | 27 | (def ^:const empty-log-file "./test/code_maat/end_to_end/empty.xml") 28 | (def ^:const empty-git-file "./test/code_maat/end_to_end/empty.git") 29 | (def ^:const empty-hg-file "./test/code_maat/end_to_end/empty.hg") 30 | (def ^:const empty-p4-file "./test/code_maat/end_to_end/empty.p4") 31 | 32 | (def shared-options 33 | (merge 34 | test-data/options-with-low-thresholds 35 | {:age-time-now "2015-03-01"})) 36 | 37 | (defn- make-options-for 38 | [vcs analysis] 39 | (merge 40 | shared-options 41 | {:version-control vcs 42 | :analysis analysis})) 43 | 44 | (defn- svn-csv-options 45 | [analysis] 46 | (make-options-for "svn" analysis)) 47 | 48 | (defn- git-options 49 | [analysis] 50 | (make-options-for "git" analysis)) 51 | 52 | (defn- git2-options 53 | [analysis] 54 | (make-options-for "git2" analysis)) 55 | 56 | (defn- hg-options 57 | [analysis] 58 | (make-options-for "hg" analysis)) 59 | 60 | (defn- p4-options 61 | [analysis] 62 | (make-options-for "p4" analysis)) 63 | 64 | (def-data-driven-with-vcs-test analysis-of-authors 65 | [[svn-log-file (svn-csv-options "authors")] 66 | [git-log-file (git-options "authors")] 67 | [git2-log-file (git2-options "authors")] 68 | [p4-log-file (p4-options "authors")] 69 | [hg-log-file (hg-options "authors")]] 70 | (is (= (run-with-str-output log-file options) 71 | "entity,n-authors,n-revs\n/Infrastrucure/Network/Connection.cs,2,2\n/Presentation/Status/ClientPresenter.cs,1,1\n"))) 72 | 73 | (def-data-driven-with-vcs-test analysis-of-revisions 74 | [[svn-log-file (svn-csv-options "revisions")] 75 | [git-log-file (git-options "revisions")] 76 | [git2-log-file (git2-options "revisions")] 77 | [p4-log-file (p4-options "revisions")] 78 | [hg-log-file (hg-options "revisions")]] 79 | (is (= (run-with-str-output log-file options) 80 | "entity,n-revs\n/Infrastrucure/Network/Connection.cs,2\n/Presentation/Status/ClientPresenter.cs,1\n"))) 81 | 82 | (def-data-driven-with-vcs-test analysis-of-coupling 83 | [[svn-log-file (svn-csv-options "coupling")] 84 | [git-log-file (git-options "coupling")] 85 | [git2-log-file (git2-options "coupling")] 86 | [p4-log-file (p4-options "coupling")] 87 | [hg-log-file (hg-options "coupling")]] 88 | (is (= (run-with-str-output log-file options) 89 | "entity,coupled,degree,average-revs\n/Infrastrucure/Network/Connection.cs,/Presentation/Status/ClientPresenter.cs,66,2\n"))) 90 | 91 | (defn- ->verbose 92 | [options] 93 | (assoc options :verbose-results true)) 94 | 95 | (def-data-driven-with-vcs-test verbose-change-coupling 96 | [[svn-log-file (-> "coupling" svn-csv-options ->verbose)] 97 | [git-log-file (-> "coupling" git-options ->verbose)] 98 | [git2-log-file (-> "coupling" git2-options ->verbose)] 99 | [p4-log-file (-> "coupling" p4-options ->verbose)] 100 | [hg-log-file (-> "coupling" hg-options ->verbose)]] 101 | (is (= (run-with-str-output log-file options) 102 | "entity,coupled,degree,average-revs,first-entity-revisions,second-entity-revisions,shared-revisions\n/Infrastrucure/Network/Connection.cs,/Presentation/Status/ClientPresenter.cs,66,2,2,1,1\n"))) 103 | 104 | (def-data-driven-with-vcs-test analysis-of-effort 105 | [[svn-log-file (svn-csv-options "entity-effort")] 106 | [git-log-file (git-options "entity-effort")] 107 | [git2-log-file (git2-options "entity-effort")] 108 | [p4-log-file (p4-options "entity-effort")] 109 | [hg-log-file (hg-options "entity-effort")]] 110 | (is (= (run-with-str-output log-file options) 111 | "entity,author,author-revs,total-revs\n/Infrastrucure/Network/Connection.cs,APT,1,2\n/Infrastrucure/Network/Connection.cs,XYZ,1,2\n/Presentation/Status/ClientPresenter.cs,APT,1,1\n"))) 112 | 113 | (def-data-driven-with-vcs-test analysis-of-communication 114 | [[svn-log-file (svn-csv-options "communication")] 115 | [git-log-file (git-options "communication")] 116 | [git2-log-file (git2-options "communication")] 117 | [p4-log-file (p4-options "communication")] 118 | [hg-log-file (hg-options "communication")]] 119 | (is (= (run-with-str-output log-file options) 120 | "author,peer,shared,average,strength\nXYZ,APT,1,2,50\nAPT,XYZ,1,2,50\n"))) 121 | 122 | (defn- with-message-to-match 123 | [options msg] 124 | (assoc options :expression-to-match msg)) 125 | 126 | (deftest counts-commit-message-patterns 127 | (is (= (run-with-str-output git-log-file 128 | (with-message-to-match (git-options "messages")"stat")) 129 | "entity,matches\n/Infrastrucure/Network/Connection.cs,1\n"))) 130 | 131 | ;;; The identity analysis is intended as a debug aid or to 132 | ;;; generate parsed VCS data as input to other tools. 133 | ;;; The idea with identity is to dump the parse result to 134 | ;;; the output. 135 | (deftest svn-identity-analysis-contains-additional-info 136 | (is (= (run-with-str-output svn-log-file (svn-csv-options "identity")) 137 | "entity,date,author,action,rev\n/Infrastrucure/Network/Connection.cs,2013-02-08,APT,M,2\n/Presentation/Status/ClientPresenter.cs,2013-02-08,APT,M,2\n/Infrastrucure/Network/Connection.cs,2013-02-07,XYZ,M,1\n"))) 138 | 139 | ;;; The git, Mercurical, and Perforce parsers do not include the 140 | ;;; 'action' tag that we have in the current SVN data. 141 | ;;; I'm likely to add it later. For now, just document 142 | ;;; the behavior here. 143 | 144 | (deftest hg-identity-analysis 145 | (is (= (run-with-str-output hg-log-file (hg-options "identity")) 146 | "author,rev,date,entity,message\nAPT,2,2013-02-08,/Infrastrucure/Network/Connection.cs,-\nAPT,2,2013-02-08,/Presentation/Status/ClientPresenter.cs,-\nXYZ,1,2013-02-07,/Infrastrucure/Network/Connection.cs,-\n"))) 147 | 148 | (deftest p4-identity-analysis 149 | (is (= (run-with-str-output p4-log-file (p4-options "identity")) 150 | "author,rev,date,entity,message\nAPT,2,2013-02-08,/Infrastrucure/Network/Connection.cs,\nAPT,2,2013-02-08,/Presentation/Status/ClientPresenter.cs,\nXYZ,1,2013-02-07,/Infrastrucure/Network/Connection.cs,\n"))) 151 | 152 | (deftest git-identity-analysis 153 | "Git included additional churn info." 154 | (is (= (run-with-str-output git-log-file (git-options "identity")) 155 | "author,rev,date,entity,message,loc-added,loc-deleted\nAPT,2,2013-02-08,/Infrastrucure/Network/Connection.cs,git: authors and revisions implemented,1,2\nAPT,2,2013-02-08,/Presentation/Status/ClientPresenter.cs,git: authors and revisions implemented,3,4\nXYZ,1,2013-02-07,/Infrastrucure/Network/Connection.cs,Report connection status,18,2\n"))) 156 | 157 | ;; All age tests are run against a fixed 'now' time specified in the options. 158 | (def-data-driven-with-vcs-test analysis-of-code-age 159 | [[svn-log-file (svn-csv-options "age")] 160 | [git-log-file (git-options "age")] 161 | [git2-log-file (git2-options "age")] 162 | [p4-log-file (p4-options "age")] 163 | [hg-log-file (hg-options "age")]] 164 | (is (= (run-with-str-output log-file options) 165 | "entity,age-months\n/Infrastrucure/Network/Connection.cs,24\n/Presentation/Status/ClientPresenter.cs,24\n"))) 166 | 167 | (deftest reports-invalid-arguments 168 | (testing "Non-existent input file" 169 | (is (thrown? IllegalArgumentException (app/run "I/do/not/exist" {})))) 170 | (testing "Missing mandatory options (normally added by the front)" 171 | (is (thrown? IllegalArgumentException (app/run svn-log-file {:analysis "coupling"}))))) 172 | 173 | (def-data-driven-with-vcs-test analysis-of-authors-with-empty-log 174 | [[empty-log-file (svn-csv-options "authors")] 175 | [empty-git-file (git-options "authors")] 176 | [empty-p4-file (p4-options "authors")] 177 | [empty-hg-file (hg-options "authors")]] 178 | (is (= (run-with-str-output log-file options) 179 | "entity,n-authors,n-revs\n"))) 180 | 181 | (def-data-driven-with-vcs-test analysis-of-coupling-with-empty-log 182 | [[empty-log-file (svn-csv-options "coupling")] 183 | [empty-git-file (git-options "coupling")] 184 | [empty-p4-file (p4-options "coupling")] 185 | [empty-hg-file (hg-options "coupling")]] 186 | (is (= (run-with-str-output log-file options) 187 | "entity,coupled,degree,average-revs\n"))) 188 | 189 | (def-data-driven-with-vcs-test analysis-of-revisions-with-empty-log 190 | [[empty-log-file (svn-csv-options "revisions")] 191 | [empty-git-file (git-options "revisions")] 192 | [empty-p4-file (p4-options "revisions")] 193 | [empty-hg-file (hg-options "revisions")]] 194 | (is (= (run-with-str-output log-file options) 195 | "entity,n-revs\n"))) 196 | 197 | (def-data-driven-with-vcs-test analysis-of-effort-with-empty-log 198 | [[empty-log-file (svn-csv-options "entity-effort")] 199 | [empty-git-file (git-options "entity-effort")] 200 | [empty-p4-file (p4-options "entity-effort")] 201 | [empty-hg-file (hg-options "entity-effort")]] 202 | (is (= (run-with-str-output log-file options) 203 | "entity,author,author-revs,total-revs\n"))) 204 | 205 | (def-data-driven-with-vcs-test analysis-of-communication-with-empty-log 206 | [[empty-log-file (svn-csv-options "communication")] 207 | [empty-git-file (git-options "communication")] 208 | [empty-p4-file (p4-options "communication")] 209 | [empty-hg-file (hg-options "communication")]] 210 | (is (= (run-with-str-output log-file options) 211 | "author,peer,shared,average,strength\n"))) 212 | 213 | (def-data-driven-with-vcs-test analysis-of-code-age-with-empty-log 214 | [[empty-log-file (svn-csv-options "age")] 215 | [empty-git-file (git-options "age")] 216 | [empty-p4-file (p4-options "age")] 217 | [empty-hg-file (hg-options "age")]] 218 | (is (= (run-with-str-output log-file options) 219 | "entity,age-months\n"))) 220 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | APT 6 | 2013-02-08T11:46:13.844538Z 7 | 8 | /Infrastrucure/Network/Connection.cs 11 | 12 | /Presentation/Status/ClientPresenter.cs 15 | 16 | 17 | [bug] Fixed the connection status 18 | 19 | 21 | XYZ 22 | 2013-02-07T11:46:13.844538Z 23 | 24 | /Infrastrucure/Network/Connection.cs 27 | 28 | 29 | [feature] Report connection status 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/simple_git.txt: -------------------------------------------------------------------------------- 1 | [2] APT 2013-02-08 git: authors and revisions implemented 2 | 1 2 /Infrastrucure/Network/Connection.cs 3 | 3 4 /Presentation/Status/ClientPresenter.cs 4 | 5 | [1] XYZ 2013-02-07 Report connection status 6 | 18 2 /Infrastrucure/Network/Connection.cs 7 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/simple_git2.txt: -------------------------------------------------------------------------------- 1 | --2--2013-02-08--APT 2 | 1 2 /Infrastrucure/Network/Connection.cs 3 | 3 4 /Presentation/Status/ClientPresenter.cs 4 | 5 | --1--2013-02-07--XYZ 6 | 18 2 /Infrastrucure/Network/Connection.cs 7 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/simple_hg.txt: -------------------------------------------------------------------------------- 1 | rev: 2 author: APT date: 2013-02-08 files: 2 | /Infrastrucure/Network/Connection.cs 3 | /Presentation/Status/ClientPresenter.cs 4 | 5 | rev: 1 author: XYZ date: 2013-02-07 files: 6 | /Infrastrucure/Network/Connection.cs 7 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/simple_p4.txt: -------------------------------------------------------------------------------- 1 | Change 2 by APT@client on 2013/02/08 16:15:09 2 | Comments for 2 3 | Affected files ... 4 | ... //depot/project/Infrastrucure/Network/Connection.cs#9 edit 5 | ... //depot/project/Presentation/Status/ClientPresenter.cs#15 edit 6 | 7 | Change 1 by XYZ@client on 2013/02/07 10:37:58 8 | HM-2728 white space +review HMR-319 9 | Affected files ... 10 | ... //depot/project/Infrastrucure/Network/Connection.cs#13 edit -------------------------------------------------------------------------------- /test/code_maat/end_to_end/svn_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.svn-live-data-test 7 | (:require [code-maat.app.app :as app] 8 | [clj-time.core :as clj-time] 9 | [code-maat.analysis.test-data :as test-data]) 10 | (:use clojure.test)) 11 | 12 | (def ^:const statsvn-log-file "./test/code_maat/end_to_end/statsvn.log") 13 | 14 | (deftest parses-live-data 15 | (testing "StatSvn: this file has a different format (no kind-attribute on the paths)" 16 | (is (= (with-out-str 17 | (app/run statsvn-log-file 18 | {:version-control "svn" 19 | :analysis "authors" 20 | :rows 2})) 21 | "entity,n-authors,n-revs\n/trunk/statsvn/src/net/sf/statcvs/Main.java,4,19\n/trunk/statsvn/src/net/sf/statcvs/input/Builder.java,4,18\n")))) 22 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/team_level_analyses_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2017 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.team-level-analyses-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:private git-log-file "./test/code_maat/end_to_end/mono_git.log") 11 | 12 | (deftest runs-the-analyses-on-team-level 13 | (testing "Translates individuals to team according to the provided CSV file with mappings" 14 | (is (= (with-out-str 15 | (app/run git-log-file 16 | {:version-control "git" 17 | :analysis "main-dev" 18 | :team-map-file "./test/code_maat/end_to_end/mono_git_team_map.csv" 19 | :rows 3})) 20 | "entity,main-dev,added,total-added,ownership\neglib/src/gfile-posix.c,Another Team,1,1,1.0\nmcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs,Mono,4,4,1.0\nmcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs,Mono,11,11,1.0\n"))) 21 | (testing "Compare the above test to the same analysis on individual authors" 22 | (is (= (with-out-str 23 | (app/run git-log-file 24 | {:version-control "git" 25 | :analysis "main-dev" 26 | :rows 3})) 27 | "entity,main-dev,added,total-added,ownership\neglib/src/gfile-posix.c,Rodrigo Kumpera,1,1,1.0\nmcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs,Lluis Sanchez,4,4,1.0\nmcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs,Lluis Sanchez,11,11,1.0\n")))) 28 | 29 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/text-layers-definition.txt: -------------------------------------------------------------------------------- 1 | src/InteractiveWindow => Interactive Layer 2 | src/EditorFeatures => Editor Layer 3 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/tfs.log: -------------------------------------------------------------------------------- 1 | ----------------------------------------------------------------------------------------------------------------------- 2 | Changeset: 71 3 | User: Ryan Coy 4 | Date: Wednesday, March 9, 2016 3:13:45 PM 5 | 6 | Comment: 7 | Additional Tinkering 8 | 9 | Items: 10 | edit $/Project/Project/Project/View/MainWindow.xaml 11 | edit $/Project/Project/Project/ViewModel/MainViewModel.cs 12 | 13 | ----------------------------------------------------------------------------------------------------------------------- 14 | Changeset: 70 15 | User: Ryan Coy 16 | Checked in by: bld.srvr 17 | Date: Monday, March 7, 2016 9:55:33 PM 18 | 19 | Comment: 20 | Quick UI Tweaks 21 | 22 | Better ViewModel Layout 23 | 24 | Items: 25 | edit $/Project/Project/Project/View/MainWindow.xaml 26 | edit $/Project/Project/Project/ViewModel/MainViewModel.cs 27 | 28 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/tfs_live_data_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2016 Ryan Coy 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.end-to-end.tfs-live-data-test 7 | (:require [code-maat.app.app :as app]) 8 | (:use clojure.test)) 9 | 10 | (def ^:const tfs-log-file "./test/code_maat/end_to_end/tfs.log") 11 | 12 | (deftest parses-live-data 13 | (is (= (with-out-str 14 | (app/run tfs-log-file 15 | {:version-control "tfs" 16 | :analysis "revisions" 17 | :rows 2})) 18 | "entity,n-revs\n/Project/Project/Project/View/MainWindow.xaml,2\n/Project/Project/Project/ViewModel/MainViewModel.cs,2\n"))) 19 | -------------------------------------------------------------------------------- /test/code_maat/end_to_end/tpp_hg.log: -------------------------------------------------------------------------------- 1 | rev: 49 author: apn date: 2011-05-31 files: 2 | .hgtags 3 | 4 | rev: 48 author: apn date: 2011-05-31 files: 5 | impl/CMakeLists.txt 6 | test/CMakeLists.txt 7 | 8 | rev: 47 author: apn date: 2010-08-29 files: 9 | .hgtags 10 | 11 | rev: 46 author: apn date: 2010-08-29 files: 12 | impl/rpc.cpp 13 | test/CMakeLists.txt 14 | 15 | rev: 45 author: apn date: 2010-08-24 files: 16 | impl/constants.cpp 17 | impl/erl_any.cpp 18 | impl/erlang_value_types.cpp 19 | impl/exceptions.cpp 20 | impl/ext_term_grammar.h 21 | impl/types.cpp 22 | impl/types.h 23 | test/patterns.cpp 24 | test/patterns_testing_any.cpp 25 | test/patterns_testing_assign.cpp 26 | tinch_pp/erlang_value_types.h 27 | tinch_pp/exceptions.h 28 | 29 | rev: 44 author: apn date: 2010-07-16 files: 30 | impl/rpc.cpp 31 | test/rpc_test.cpp 32 | tinch_pp/rpc.h 33 | 34 | rev: 43 author: apn date: 2010-07-15 files: 35 | impl/erl_any.cpp 36 | impl/erlang_value_types.cpp 37 | impl/ext_term_grammar.h 38 | impl/types.h 39 | test/patterns.cpp 40 | test/patterns_testing_any.cpp 41 | test/patterns_testing_assign.cpp 42 | tinch_pp/erlang_value_types.h 43 | 44 | rev: 42 author: apn date: 2010-07-14 files: 45 | CMakeLists.txt 46 | impl/CMakeLists.txt 47 | impl/actual_mailbox.cpp 48 | impl/actual_node.cpp 49 | impl/actual_node.h 50 | impl/ctrl_msg_dispatcher.cpp 51 | impl/epmd_requestor.cpp 52 | impl/erl_any.cpp 53 | impl/erl_cpp_exception.cpp 54 | impl/erl_cpp_exception.h 55 | impl/exceptions.cpp 56 | impl/node.cpp 57 | impl/node_connection.cpp 58 | impl/node_connection_state.cpp 59 | impl/node_connector.cpp 60 | impl/rpc.cpp 61 | impl/utils.cpp 62 | impl/utils.h 63 | test/chat_client.cpp 64 | test/local_link.cpp 65 | test/mbox_same_node_links.cpp 66 | test/net_kernel_sim.cpp 67 | test/patterns.cpp 68 | test/patterns_testing_any.cpp 69 | test/patterns_testing_assign.cpp 70 | test/remote_link.cpp 71 | test/rpc_test.cpp 72 | test/thread_safe_queue.cpp 73 | tinch_pp/CMakeLists.txt 74 | tinch_pp/exceptions.h 75 | tinch_pp/node.h 76 | tinch_pp/rpc.h 77 | 78 | rev: 41 author: apn date: 2010-07-13 files: 79 | impl/actual_mailbox.cpp 80 | impl/actual_mailbox.h 81 | impl/control_msg_exit.h 82 | impl/control_msg_exit2.h 83 | impl/control_msg_link.h 84 | impl/control_msg_reg_send.h 85 | impl/control_msg_send.h 86 | impl/control_msg_unlink.h 87 | impl/control_msgs_impl.cpp 88 | impl/ctrl_msg_dispatcher.cpp 89 | impl/erl_any.cpp 90 | impl/erl_cpp_exception.cpp 91 | impl/erl_cpp_exception.h 92 | impl/erl_string.cpp 93 | impl/erlang_value_types.cpp 94 | impl/ext_message_builder.cpp 95 | impl/ext_message_builder.h 96 | impl/ext_term_grammar.h 97 | impl/link_policies.cpp 98 | impl/link_policies.h 99 | impl/linker.cpp 100 | impl/linker.h 101 | impl/mailbox_controller_type.h 102 | impl/node.cpp 103 | impl/node_access.h 104 | impl/node_connection.cpp 105 | impl/node_connection.h 106 | impl/node_connection_access.h 107 | impl/node_connection_state.cpp 108 | impl/node_connection_state.h 109 | impl/rpc.cpp 110 | impl/type_makers.cpp 111 | impl/types.cpp 112 | impl/types.h 113 | test/chat_client.cpp 114 | test/local_link.cpp 115 | test/net_kernel_sim.cpp 116 | test/patterns.cpp 117 | test/patterns_testing_any.cpp 118 | test/patterns_testing_assign.cpp 119 | test/remote_link.cpp 120 | test/rpc_test.cpp 121 | tinch_pp/erl_any.h 122 | tinch_pp/erl_string.h 123 | tinch_pp/erl_tuple.h 124 | tinch_pp/erlang_value_types.h 125 | tinch_pp/mailbox.h 126 | tinch_pp/make_erl_tuple.h 127 | tinch_pp/node.h 128 | tinch_pp/type_makers.h 129 | 130 | rev: 40 author: apn date: 2010-06-02 files: 131 | CMakeLists.txt 132 | 133 | rev: 39 author: apn date: 2010-05-31 files: 134 | impl/link_policies.cpp 135 | impl/node.cpp 136 | 137 | rev: 38 author: apn date: 2010-05-31 files: 138 | .hgtags 139 | 140 | rev: 37 author: apn date: 2010-05-31 files: 141 | impl/linker.cpp 142 | impl/node_connection_state.cpp 143 | test/local_link.cpp 144 | test/remote_link.cpp 145 | 146 | rev: 36 author: apn date: 2010-05-16 files: 147 | impl/CMakeLists.txt 148 | impl/actual_mailbox.cpp 149 | impl/link_policies.cpp 150 | impl/link_policies.h 151 | impl/node.cpp 152 | impl/node_access.h 153 | test/mbox_same_node_links.cpp 154 | test/remote_link.cpp 155 | tinch_pp/node.h 156 | 157 | rev: 35 author: apn date: 2010-05-15 files: 158 | impl/linker.cpp 159 | impl/node.cpp 160 | test/CMakeLists.txt 161 | test/mbox_same_node_links.cpp 162 | tinch_pp/node.h 163 | 164 | rev: 34 author: apn date: 2010-05-13 files: 165 | impl/CMakeLists.txt 166 | impl/actual_mailbox.cpp 167 | impl/actual_mailbox.h 168 | impl/control_msg.h 169 | impl/control_msg_exit.h 170 | impl/control_msg_exit2.h 171 | impl/control_msg_link.h 172 | impl/control_msg_reg_send.h 173 | impl/control_msg_send.h 174 | impl/control_msg_unlink.h 175 | impl/control_msgs_impl.cpp 176 | impl/ext_message_builder.cpp 177 | impl/ext_message_builder.h 178 | impl/linker.cpp 179 | impl/linker.h 180 | impl/mailbox_controller_type.h 181 | impl/node.cpp 182 | impl/node_access.h 183 | impl/node_connection.cpp 184 | impl/node_connection.h 185 | impl/node_connection_state.cpp 186 | impl/node_connection_state.h 187 | test/link_tester.erl 188 | test/local_link.cpp 189 | test/remote_link.cpp 190 | tinch_pp/mailbox.h 191 | tinch_pp/node.h 192 | 193 | rev: 33 author: apn date: 2010-04-14 files: 194 | impl/CMakeLists.txt 195 | impl/actual_mailbox.cpp 196 | impl/actual_mailbox.h 197 | impl/constants.cpp 198 | impl/constants.h 199 | impl/ctrl_msg_dispatcher.cpp 200 | impl/erl_cpp_exception.cpp 201 | impl/erl_cpp_exception.h 202 | impl/ext_term_grammar.h 203 | impl/linker.cpp 204 | impl/linker.h 205 | impl/mailbox_controller_type.h 206 | impl/node.cpp 207 | impl/node_access.h 208 | impl/node_connection.cpp 209 | impl/node_connection.h 210 | impl/node_connection_access.h 211 | impl/types.h 212 | test/CMakeLists.txt 213 | test/link_tester.erl 214 | test/local_link.cpp 215 | test/remote_link.cpp 216 | tinch_pp/mailbox.h 217 | tinch_pp/node.h 218 | 219 | rev: 32 author: apn date: 2010-04-03 files: 220 | impl/node.cpp 221 | tinch_pp/node.h 222 | 223 | rev: 31 author: apn date: 2010-04-03 files: 224 | test/CMakeLists.txt 225 | 226 | rev: 30 author: David Åberg date: 2010-03-21 files: 227 | CMakeLists.txt 228 | test/CMakeLists.txt 229 | 230 | rev: 29 author: apn date: 2010-03-16 files: 231 | .hgtags 232 | 233 | rev: 28 author: apn date: 2010-03-15 files: 234 | tinch_pp/erl_any.h 235 | tinch_pp/erl_list.h 236 | tinch_pp/erl_object.h 237 | tinch_pp/erl_string.h 238 | tinch_pp/erl_tuple.h 239 | tinch_pp/erlang_types.h 240 | tinch_pp/erlang_value_types.h 241 | tinch_pp/mailbox.h 242 | tinch_pp/make_erl_tuple.h 243 | tinch_pp/matchable.h 244 | tinch_pp/node.h 245 | tinch_pp/rpc.h 246 | tinch_pp/type_makers.h 247 | 248 | rev: 27 author: apn date: 2010-03-15 files: 249 | tinch_pp/make_erl_tuple.h 250 | 251 | rev: 26 author: apn date: 2010-03-15 files: 252 | impl/actual_mailbox.cpp 253 | impl/actual_mailbox.h 254 | impl/node.cpp 255 | impl/node_access.h 256 | test/patterns.cpp 257 | tinch_pp/mailbox.h 258 | tinch_pp/node.h 259 | 260 | rev: 25 author: apn date: 2010-03-15 files: 261 | test/CMakeLists.txt 262 | test/net_kernel_sim.cpp 263 | test/patterns.cpp 264 | 265 | rev: 24 author: David Åberg date: 2010-03-11 files: 266 | impl/CMakeLists.txt 267 | 268 | rev: 23 author: David Åberg date: 2010-03-11 files: 269 | test/CMakeLists.txt 270 | 271 | rev: 22 author: David Åberg date: 2010-03-11 files: 272 | Erlang.cmake 273 | test/CMakeLists.txt 274 | 275 | rev: 21 author: David Åberg date: 2010-03-11 files: 276 | test/CMakeLists.txt 277 | test/patterns.cpp 278 | 279 | rev: 20 author: David Åberg date: 2010-03-09 files: 280 | CMakeLists.txt 281 | Doxyfile 282 | Doxyfile.in 283 | 284 | rev: 19 author: David Åberg date: 2010-03-09 files: 285 | test/CMakeLists.txt 286 | test/patterns.cpp 287 | 288 | rev: 18 author: David Åberg date: 2010-03-08 files: 289 | test/patterns.cpp 290 | 291 | rev: 17 author: freke@localhost.localdomain date: 2010-03-06 files: 292 | test/CMakeLists.txt 293 | test/reflect_msg.erl 294 | tinch_pp/CMakeLists.txt 295 | 296 | rev: 16 author: freke@localhost.localdomain date: 2010-03-01 files: 297 | test/CMakeLists.txt 298 | 299 | rev: 15 author: freke@localhost.localdomain date: 2010-03-01 files: 300 | test/CMakeLists.txt 301 | 302 | rev: 14 author: apn date: 2010-03-10 files: 303 | impl/type_makers.cpp 304 | test/CMakeLists.txt 305 | test/chat_client.cpp 306 | test/chat_server.erl 307 | tinch_pp/type_makers.h 308 | 309 | rev: 13 author: David Ã…berg date: 2010-03-09 files: 310 | CMakeLists.txt 311 | Doxyfile 312 | Doxyfile.in 313 | 314 | rev: 12 author: apn date: 2010-03-08 files: 315 | tinch_pp/mailbox.h 316 | 317 | rev: 11 author: apn date: 2010-03-08 files: 318 | impl/actual_mailbox.cpp 319 | impl/actual_mailbox.h 320 | impl/node.cpp 321 | impl/node_access.h 322 | tinch_pp/erl_any.h 323 | tinch_pp/erl_object.h 324 | tinch_pp/erl_string.h 325 | tinch_pp/erl_tuple.h 326 | tinch_pp/erlang_types.h 327 | tinch_pp/erlang_value_types.h 328 | tinch_pp/mailbox.h 329 | tinch_pp/make_erl_tuple.h 330 | tinch_pp/matchable.h 331 | tinch_pp/node.h 332 | tinch_pp/rpc.h 333 | 334 | rev: 10 author: apn date: 2010-03-08 files: 335 | Doxyfile 336 | 337 | rev: 9 author: apn date: 2010-03-08 files: 338 | impl/erlang_value_types.cpp 339 | impl/ext_term_grammar.h 340 | test/CMakeLists.txt 341 | test/patterns_testing_any.cpp 342 | 343 | rev: 8 author: apn date: 2010-03-07 files: 344 | test/patterns_testing_assign.cpp 345 | test/reflect_msg.erl 346 | tinch_pp/CMakeLists.txt 347 | 348 | rev: 7 author: freke@localhost.localdomain date: 2010-03-01 files: 349 | test/CMakeLists.txt 350 | 351 | rev: 6 author: freke@localhost.localdomain date: 2010-03-01 files: 352 | test/CMakeLists.txt 353 | 354 | rev: 5 author: apn date: 2010-03-07 files: 355 | impl/list_matcher.h 356 | test/CMakeLists.txt 357 | test/patterns.cpp 358 | test/patterns_testing_any.cpp 359 | test/patterns_testing_assign.cpp 360 | tinch_pp/erl_list.h 361 | 362 | rev: 4 author: apn date: 2010-02-28 files: 363 | test/net_kernel_sim.cpp 364 | 365 | rev: 3 author: freke@localhost.localdomain date: 2010-02-28 files: 366 | test/CMakeLists.txt 367 | test/net_kernel_sim.cpp 368 | 369 | rev: 2 author: freke@localhost.localdomain date: 2010-02-27 files: 370 | impl/list_matcher.h 371 | test/thread_safe_queue.cpp 372 | 373 | rev: 1 author: apn date: 2010-02-26 files: 374 | impl/actual_mailbox.cpp 375 | impl/actual_mailbox.h 376 | impl/constants.cpp 377 | impl/constants.h 378 | impl/ctrl_msg_dispatcher.cpp 379 | impl/ctrl_msg_dispatcher.h 380 | impl/epmd_protocol.h 381 | impl/epmd_requestor.cpp 382 | impl/epmd_requestor.h 383 | impl/erl_any.cpp 384 | impl/erl_cpp_exception.cpp 385 | impl/erl_cpp_exception.h 386 | impl/erl_string.cpp 387 | impl/erlang_value_types.cpp 388 | impl/ext_message_builder.cpp 389 | impl/ext_message_builder.h 390 | impl/ext_term_grammar.h 391 | impl/handshake_grammar.h 392 | impl/list_matcher.h 393 | impl/matchable_range.cpp 394 | impl/matchable_range.h 395 | impl/matchable_seq.cpp 396 | impl/matchable_seq.h 397 | impl/networker.h 398 | impl/node.cpp 399 | impl/node_access.h 400 | impl/node_async_tcp_ip.cpp 401 | impl/node_async_tcp_ip.h 402 | impl/node_connection.cpp 403 | impl/node_connection.h 404 | impl/node_connection_access.h 405 | impl/node_connection_state.cpp 406 | impl/node_connection_state.h 407 | impl/node_connector.cpp 408 | impl/node_connector.h 409 | impl/node_msg.h 410 | impl/rpc.cpp 411 | impl/string_matcher.h 412 | impl/term_conversions.h 413 | impl/type_makers.cpp 414 | impl/types.cpp 415 | impl/types.h 416 | impl/utils.cpp 417 | impl/utils.h 418 | test/net_kernel_sim.cpp 419 | test/patterns.cpp 420 | test/rpc_test.cpp 421 | test/thread_safe_queue.cpp 422 | 423 | rev: 0 author: apn date: 2010-02-26 files: 424 | CMakeLists.txt 425 | impl/CMakeLists.txt 426 | impl/RefToValue.h 427 | impl/ScopeGuard.h 428 | impl/actual_mailbox.cpp 429 | impl/actual_mailbox.h 430 | impl/constants.cpp 431 | impl/constants.h 432 | impl/ctrl_msg_dispatcher.cpp 433 | impl/ctrl_msg_dispatcher.h 434 | impl/epmd_protocol.h 435 | impl/epmd_requestor.cpp 436 | impl/epmd_requestor.h 437 | impl/erl_any.cpp 438 | impl/erl_cpp_exception.cpp 439 | impl/erl_cpp_exception.h 440 | impl/erl_string.cpp 441 | impl/erlang_value_types.cpp 442 | impl/ext_message_builder.cpp 443 | impl/ext_message_builder.h 444 | impl/ext_term_grammar.h 445 | impl/handshake_grammar.h 446 | impl/list_matcher.h 447 | impl/matchable_range.cpp 448 | impl/matchable_range.h 449 | impl/matchable_seq.cpp 450 | impl/matchable_seq.h 451 | impl/md5.cpp 452 | impl/md5.h 453 | impl/networker.h 454 | impl/node.cpp 455 | impl/node_access.h 456 | impl/node_async_tcp_ip.cpp 457 | impl/node_async_tcp_ip.h 458 | impl/node_connection.cpp 459 | impl/node_connection.h 460 | impl/node_connection_access.h 461 | impl/node_connection_state.cpp 462 | impl/node_connection_state.h 463 | impl/node_connector.cpp 464 | impl/node_connector.h 465 | impl/node_msg.h 466 | impl/rpc.cpp 467 | impl/string_matcher.h 468 | impl/term_conversions.h 469 | impl/type_makers.cpp 470 | impl/types.cpp 471 | impl/types.h 472 | impl/utils.cpp 473 | impl/utils.h 474 | test/CMakeLists.txt 475 | test/net_kernel_sim.cpp 476 | test/patterns.cpp 477 | test/rpc_test.cpp 478 | test/thread_safe_queue.cpp 479 | tinch_pp/CMakeLists.txt 480 | tinch_pp/erl_any.h 481 | tinch_pp/erl_list.h 482 | tinch_pp/erl_object.h 483 | tinch_pp/erl_string.h 484 | tinch_pp/erl_tuple.h 485 | tinch_pp/erlang_types.h 486 | tinch_pp/erlang_value_types.h 487 | tinch_pp/mailbox.h 488 | tinch_pp/make_erl_tuple.h 489 | tinch_pp/matchable.h 490 | tinch_pp/node.h 491 | tinch_pp/rpc.h 492 | tinch_pp/type_makers.h 493 | 494 | -------------------------------------------------------------------------------- /test/code_maat/parsers/git2_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.git2-test 7 | (:require [code-maat.parsers.git2 :as git]) 8 | (:use clojure.test incanter.core)) 9 | 10 | (def ^:const entry 11 | "--990442e--2013-08-29--Adam Petersen 12 | 1 0 project.clj 13 | 2 4 src/code_maat/parsers/git.clj 14 | ") 15 | 16 | (def ^:const binary-entry 17 | "--990442e--2013-11-10--Adam Petersen 18 | - - project.bin 19 | 2 40 src/code_maat/parsers/git.clj 20 | ") 21 | 22 | (def ^:const entries 23 | "--b777738--2013-08-29--Adam Petersen 24 | 10 9 src/code_maat/parsers/git.clj 25 | 32 0 test/code_maat/parsers/git_test.clj 26 | 27 | --a527b79--2013-08-29--Adam Petersen 28 | 6 2 src/code_maat/parsers/git.clj 29 | 0 7 test/code_maat/end_to_end/scenario_tests.clj 30 | 18 0 test/code_maat/end_to_end/simple_git.txt 31 | 21 0 test/code_maat/end_to_end/svn_live_data_test.clj 32 | ") 33 | 34 | (def ^:const pull-requests 35 | "--0d3de0c--2013-01-04--Mr X 36 | --77c8751--2013-01-04--Mr Y 37 | 1 1 build.xml 38 | 1 1 project/Versions.scala 39 | ") 40 | 41 | (defn- parse 42 | [text] 43 | (git/parse-read-log text {})) 44 | 45 | (deftest parses-single-entry-to-dataset 46 | (is (= (parse entry) 47 | [{:loc-deleted "0" 48 | :loc-added "1" 49 | :author "Adam Petersen" 50 | :rev "990442e" 51 | :date "2013-08-29" 52 | :entity "project.clj" 53 | :message "-"} 54 | {:loc-deleted "4" 55 | :loc-added "2" 56 | :author "Adam Petersen" 57 | :rev "990442e" 58 | :date "2013-08-29" 59 | :entity "src/code_maat/parsers/git.clj" 60 | :message "-"}]))) 61 | 62 | (deftest parses-entry-with-binary-to-dataset 63 | "The churn for binary entries are given as a dash (-)." 64 | (is (= (parse binary-entry) 65 | [{:loc-deleted "-" 66 | :loc-added "-" 67 | :author "Adam Petersen" 68 | :rev "990442e" 69 | :date "2013-11-10" 70 | :entity "project.bin" 71 | :message "-"} 72 | {:loc-deleted "40" 73 | :loc-added "2" 74 | :author "Adam Petersen" 75 | :rev "990442e" 76 | :date "2013-11-10" 77 | :entity "src/code_maat/parsers/git.clj" 78 | :message "-"}]))) 79 | 80 | (deftest parses-multiple-entries-to-dataset 81 | (is (= (parse entries) 82 | [{:loc-deleted "9" :loc-added "10" 83 | :author "Adam Petersen" :rev "b777738" :date "2013-08-29" 84 | :entity "src/code_maat/parsers/git.clj" 85 | :message "-"} 86 | {:loc-deleted "0" :loc-added "32" 87 | :author "Adam Petersen" :rev "b777738" :date "2013-08-29" 88 | :entity "test/code_maat/parsers/git_test.clj" 89 | :message "-"} 90 | {:loc-deleted "2" :loc-added "6" 91 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 92 | :entity "src/code_maat/parsers/git.clj" 93 | :message "-"} 94 | {:loc-deleted "7" :loc-added "0" 95 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 96 | :entity "test/code_maat/end_to_end/scenario_tests.clj" 97 | :message "-"} 98 | {:loc-deleted "0" :loc-added "18", 99 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 100 | :entity "test/code_maat/end_to_end/simple_git.txt" 101 | :message "-"} 102 | {:loc-deleted "0" :loc-added "21" 103 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 104 | :entity "test/code_maat/end_to_end/svn_live_data_test.clj" 105 | :message "-"}]))) 106 | 107 | (deftest parses-empty-log-to-empty-dataset 108 | (is (= (parse "") 109 | []))) 110 | 111 | (deftest parses-pull-requests 112 | "Regression test for a parse bug: there was ambiguity in the grammar and 113 | we failed to parse a message correctly when it contained a date on the 114 | same format as the one we expect in the real date field." 115 | (is (= (parse pull-requests) 116 | [{:loc-deleted "1" 117 | :loc-added "1" 118 | :author "Mr Y" 119 | :rev "77c8751" 120 | :date "2013-01-04" 121 | :entity "build.xml" 122 | :message "-"} 123 | {:loc-deleted "1" 124 | :loc-added "1" 125 | :author "Mr Y" 126 | :rev "77c8751" 127 | :date "2013-01-04" 128 | :entity "project/Versions.scala" 129 | :message "-"}]))) 130 | -------------------------------------------------------------------------------- /test/code_maat/parsers/git_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.git-test 7 | (:require [code-maat.parsers.git :as git]) 8 | (:use clojure.test incanter.core)) 9 | 10 | ;;; I sure could have used a more minimalistic set of data. 11 | ;;; Instead I went with some live data from my projects. 12 | ;;; Better ecological validity - we're still just testing 13 | ;;; one module though. 14 | 15 | (def ^:const entry 16 | "[990442e] Adam Petersen 2013-08-29 Adapted the grammar after live tests (git) 17 | 1 0 project.clj 18 | 2 4 src/code_maat/parsers/git.clj 19 | ") 20 | 21 | (def ^:const binary-entry 22 | "[990442e] Adam Petersen 2013-11-10 Testing binary entries 23 | - - project.bin 24 | 2 40 src/code_maat/parsers/git.clj 25 | ") 26 | 27 | (def ^:const entries 28 | "[b777738] Adam Petersen 2013-08-29 git: parse merges and reverts too (grammar change) 29 | 10 9 src/code_maat/parsers/git.clj 30 | 32 0 test/code_maat/parsers/git_test.clj 31 | 32 | [a527b79] Adam Petersen 2013-08-29 git: proper error messages from instaparse 33 | 6 2 src/code_maat/parsers/git.clj 34 | 0 7 test/code_maat/end_to_end/scenario_tests.clj 35 | 18 0 test/code_maat/end_to_end/simple_git.txt 36 | 21 0 test/code_maat/end_to_end/svn_live_data_test.clj 37 | 38 | [a32793d] Ola Flisbäck 2015-09-29 Corrected date of self-awareness to 1997-08-29 39 | 1 1 README.md 40 | ") 41 | 42 | (def ^:const pull-requests 43 | "[0d3de0c] Mr X 2013-01-04 Merge pull request #1841 from adriaanm/rebase-6827-2.10.x 44 | [77c8751] Mr Y 2013-01-04 SI-6915 Updates copyright properties to 2002-2013 45 | 1 1 build.xml 46 | 1 1 project/Versions.scala 47 | ") 48 | 49 | (defn- parse 50 | [text] 51 | (git/parse-read-log text {})) 52 | 53 | (deftest parses-single-entry-to-dataset 54 | (is (= (parse entry) 55 | [{:loc-deleted "0" 56 | :loc-added "1" 57 | :author "Adam Petersen" 58 | :rev "990442e" 59 | :date "2013-08-29" 60 | :entity "project.clj" 61 | :message "Adapted the grammar after live tests (git)"} 62 | {:loc-deleted "4" 63 | :loc-added "2" 64 | :author "Adam Petersen" 65 | :rev "990442e" 66 | :date "2013-08-29" 67 | :entity "src/code_maat/parsers/git.clj" 68 | :message "Adapted the grammar after live tests (git)"}]))) 69 | 70 | (deftest parses-entry-with-binary-to-dataset 71 | "The churn for binary entries are given as a dash (-)." 72 | (is (= (parse binary-entry) 73 | [{:loc-deleted "-" 74 | :loc-added "-" 75 | :author "Adam Petersen" 76 | :rev "990442e" 77 | :date "2013-11-10" 78 | :entity "project.bin" 79 | :message "Testing binary entries"} 80 | {:loc-deleted "40" 81 | :loc-added "2" 82 | :author "Adam Petersen" 83 | :rev "990442e" 84 | :date "2013-11-10" 85 | :entity "src/code_maat/parsers/git.clj" 86 | :message "Testing binary entries"}]))) 87 | 88 | (deftest parses-multiple-entries-to-dataset 89 | (is (= (parse entries) 90 | [{:loc-deleted "9" :loc-added "10" 91 | :author "Adam Petersen" :rev "b777738" :date "2013-08-29" 92 | :entity "src/code_maat/parsers/git.clj" 93 | :message "git: parse merges and reverts too (grammar change)"} 94 | {:loc-deleted "0" :loc-added "32" 95 | :author "Adam Petersen" :rev "b777738" :date "2013-08-29" 96 | :entity "test/code_maat/parsers/git_test.clj" 97 | :message "git: parse merges and reverts too (grammar change)"} 98 | {:loc-deleted "2" :loc-added "6" 99 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 100 | :entity "src/code_maat/parsers/git.clj" 101 | :message "git: proper error messages from instaparse"} 102 | {:loc-deleted "7" :loc-added "0" 103 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 104 | :entity "test/code_maat/end_to_end/scenario_tests.clj" 105 | :message "git: proper error messages from instaparse"} 106 | {:loc-deleted "0" :loc-added "18", 107 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 108 | :entity "test/code_maat/end_to_end/simple_git.txt" 109 | :message "git: proper error messages from instaparse"} 110 | {:loc-deleted "0" :loc-added "21" 111 | :author "Adam Petersen" :rev "a527b79" :date "2013-08-29" 112 | :entity "test/code_maat/end_to_end/svn_live_data_test.clj" 113 | :message "git: proper error messages from instaparse"} 114 | {:loc-deleted "1" :loc-added "1", 115 | :author "Ola Flisbäck" :rev "a32793d" :date "2015-09-29" 116 | :entity "README.md" 117 | :message "Corrected date of self-awareness to 1997-08-29"}]))) 118 | 119 | (deftest parses-empty-log-to-empty-dataset 120 | (is (= (parse "") 121 | []))) 122 | 123 | (deftest parses-pull-requests 124 | "Regression test for a parse bug: there was ambiguity in the grammar and 125 | we failed to parse a message correctly when it contained a date on the 126 | same format as the one we expect in the real date field." 127 | (is (= (parse pull-requests) 128 | [{:loc-deleted "1" 129 | :loc-added "1" 130 | :author "Mr Y" 131 | :rev "77c8751" 132 | :date "2013-01-04" 133 | :entity "build.xml" 134 | :message "SI-6915 Updates copyright properties to 2002-2013"} 135 | {:loc-deleted "1" 136 | :loc-added "1" 137 | :author "Mr Y" 138 | :rev "77c8751" 139 | :date "2013-01-04" 140 | :entity "project/Versions.scala" 141 | :message "SI-6915 Updates copyright properties to 2002-2013"}]))) 142 | 143 | (def ^:private message-with-date ; Issue #35: Invalid argument/parse error with dates in Git commit messages 144 | "[611a2fe] User2 2016-03-11 (JIRA-789) Some text (see mails of 2016-03-11). 145 | 12\t3\tProject.UnitTests/Spec.cs 146 | 3\t3\tOtherProject.UnitTests/OtherSpec.cs 147 | ") 148 | 149 | (deftest ignores-dates-in-commit-messages 150 | (is (= (parse message-with-date) 151 | [{:author "User2" 152 | :date "2016-03-11" 153 | :entity "Project.UnitTests/Spec.cs" 154 | :loc-added "12" 155 | :loc-deleted "3" 156 | :message "(JIRA-789) Some text (see mails of 2016-03-11)." 157 | :rev "611a2fe"} 158 | {:author "User2" 159 | :date "2016-03-11" 160 | :entity "OtherProject.UnitTests/OtherSpec.cs" 161 | :loc-added "3" 162 | :loc-deleted "3" 163 | :message "(JIRA-789) Some text (see mails of 2016-03-11)." 164 | :rev "611a2fe"}]))) 165 | -------------------------------------------------------------------------------- /test/code_maat/parsers/mercurial_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.mercurial-test 7 | (:require [code-maat.parsers.mercurial :as hg]) 8 | (:use clojure.test incanter.core)) 9 | 10 | (def ^:const entry 11 | "rev: 47 author: apn date: 2010-08-29 files: 12 | .hgtags") 13 | 14 | (def ^:const entries 15 | "rev: 33 author: apn date: 2010-04-14 files: 16 | impl/CMakeLists.txt 17 | impl/actual_mailbox.cpp 18 | impl/actual_mailbox.h 19 | 20 | rev: 32 author: xyz date: 2010-04-03 files: 21 | impl/node.cpp tinch_pp/node.h") 22 | 23 | (deftest parses-single-entry-to-dataset 24 | (is (= (hg/parse-read-log entry {}) 25 | [{:author "apn " :rev "47" :date "2010-08-29" :entity ".hgtags" :message "-"}]))) 26 | 27 | (deftest parses-multiple-entries-to-dataset 28 | (is (= (hg/parse-read-log entries {}) 29 | [{:author "apn" :rev "33" :date "2010-04-14" :entity "impl/CMakeLists.txt" :message "-"} 30 | {:author "apn" :rev "33" :date "2010-04-14" :entity "impl/actual_mailbox.cpp" :message "-"} 31 | {:author "apn" :rev "33" :date "2010-04-14" :entity "impl/actual_mailbox.h" :message "-"} 32 | {:author "xyz" :rev "32" :date "2010-04-03" :entity "impl/node.cpp tinch_pp/node.h" :message "-"}]))) 33 | 34 | (deftest parses-empty-log-to-empty-dataset 35 | (is (= (hg/parse-read-log "" {}) 36 | []))) 37 | -------------------------------------------------------------------------------- /test/code_maat/parsers/perforce_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Robert Creager 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns 7 | code-maat.parsers.perforce-test 8 | (:require [code-maat.parsers.perforce :as p4]) 9 | (:use clojure.test incanter.core)) 10 | 11 | (def ^:const entry 12 | "Change 1108116 by user1@client on 2014/12/19 14:40:17 13 | Fix Stuff. 14 | More comments 15 | Affected files ... 16 | ... //depot/project/Makefile#3 edit") 17 | 18 | (def ^:const entries 19 | "Change 1108116 by user1@client on 2014/12/19 14:40:17 20 | Fix Stuff. 21 | More comments 22 | Affected files ... 23 | ... //depot/project/Makefile#3 edit 24 | 25 | Change 1108117 by user2@client on 2014/12/19 15:41:18 26 | Fix More Stuff. 27 | More comments 28 | Affected files ... 29 | ... //depot/project/meta/recipes-core/udev/udev-extraconf/mount.blacklist#2 edit") 30 | 31 | ;;; The following log sample is covered by tests as part of a bug fix. 32 | ;;; A Perforce log may have multiple job sections and we need to be 33 | ;;; able to parse them all. 34 | ;;; See Code Maat Issue 10 for more details. 35 | (def ^:const entry-with-multiple-jobs 36 | "Change 399449 by lpi001@lpi001-home-fimbul on 2015/02/17 13:26:45 37 | Ups, army bliver aldrig reduceret, har altid fuld g-dags antal 38 | Jobs fixed ... 39 | FIM-127 on 2015/03/02 by sysgen closed 40 | Ændringe i belægningen af g-dage 41 | Affected files ... 42 | ... //depot/fiks/fimbul/cerkl.cxx#100 edit") 43 | 44 | (defn- parse 45 | [text] 46 | (p4/parse-read-log text {})) 47 | 48 | (deftest parses-single-entry-to-dataset 49 | (is (= (parse entry) 50 | [{:author "user1" 51 | :rev "1108116" 52 | :date "2014-12-19" 53 | :entity "/Makefile" 54 | :message ""} 55 | ]))) 56 | 57 | (deftest parses-multiple-entries-to-dataset 58 | (is (= (parse entries) 59 | [{:author "user1" 60 | :rev "1108116" 61 | :date "2014-12-19" 62 | :entity "/Makefile" 63 | :message ""} 64 | {:author "user2" 65 | :rev "1108117" 66 | :date "2014-12-19" 67 | :entity "/meta/recipes-core/udev/udev-extraconf/mount.blacklist" 68 | :message ""} 69 | ]))) 70 | 71 | (deftest parses-empty-log-to-empty-dataset 72 | (is (= (parse "") 73 | []))) 74 | 75 | (deftest parses-entries-with-multiple-jobs 76 | (is (= (parse entry-with-multiple-jobs) 77 | [{:author "lpi001" 78 | :rev "399449" 79 | :date "2015-02-17" 80 | :entity "/fimbul/cerkl.cxx" 81 | :message ""}]))) 82 | -------------------------------------------------------------------------------- /test/code_maat/parsers/svn_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.svn-test 7 | (:require [code-maat.parsers.svn :as svn] 8 | [code-maat.parsers.xml :as xml-parser] 9 | [clojure.data.zip.xml :as zip]) 10 | (:use clojure.test)) 11 | 12 | ;;; A sample from a svn log file, served as test data to the unit tests. 13 | (def svn-log (xml-parser/string->zip " 14 | 15 | 17 | APT 18 | 2013-02-08T11:46:13.844538Z 19 | 20 | /Infrastrucure/Network/Connection.cs 23 | 24 | /Presentation/Status/ClientPresenter.cs 27 | 28 | 29 | [bug] Fixed the connection status 30 | 31 | 33 | XYZ 34 | 2013-02-07T11:46:13.844538Z 35 | 36 | /Infrastrucure/Network/Connection.cs 39 | 40 | 41 | [feature] Report connection status 42 | 43 | 44 | ")) 45 | 46 | (def log-entries (svn/zip->log-entries svn-log)) 47 | (def first-entry (first log-entries)) 48 | (def second-entry (second log-entries)) 49 | 50 | (deftest retrieves-all-entries-from-the-given-log 51 | (is (= (count log-entries) 52 | 2))) 53 | 54 | (deftest one-modified-entity-per-row 55 | (let [[row1 row2] (svn/as-rows first-entry)] 56 | (is (= row1 57 | {:entity "/Infrastrucure/Network/Connection.cs" 58 | :date "2013-02-08" 59 | :author "APT" 60 | :action "M":rev "2"})) 61 | (is (= row2 62 | {:entity "/Presentation/Status/ClientPresenter.cs" 63 | :date "2013-02-08" 64 | :author "APT" 65 | :action "M" 66 | :rev "2"})))) 67 | 68 | (deftest created-entities-are-marked 69 | (let [[row-with-created-entity] (svn/as-rows second-entry)] 70 | (is (= row-with-created-entity 71 | {:entity "/Infrastrucure/Network/Connection.cs" 72 | :date "2013-02-07" 73 | :author "XYZ" 74 | :action "A" 75 | :rev "1"})))) 76 | 77 | (deftest builds-complete-modification-history-from-log 78 | "We know from the test above that details are OK => just 79 | check the quantities." 80 | (let [modifications (svn/zip->modification-sets svn-log)] 81 | (testing "parses all items" 82 | (is (= (count modifications) 83 | 3))) 84 | (testing "parses the log info into each row" 85 | (is (= (map :author modifications) 86 | ["APT" "APT" "XYZ"])) 87 | (is (= (map :entity modifications) 88 | ["/Infrastrucure/Network/Connection.cs" 89 | "/Presentation/Status/ClientPresenter.cs" 90 | "/Infrastrucure/Network/Connection.cs"]))))) 91 | -------------------------------------------------------------------------------- /test/code_maat/parsers/tfs_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2016 Ryan Coy 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.tfs_test 7 | (:require [code-maat.parsers.tfs :as tfs]) 8 | (:use clojure.test incanter.core)) 9 | 10 | (def ^:const en-us-entry 11 | "----------------------------------------------------------------------------------------------------------------------- 12 | Changeset: 5 13 | User: Ryan Coy 14 | Date: Thursday, July 23, 2015 4:34:31 PM 15 | 16 | Comment: 17 | Created team project folder /Project via the Team Project Creation Wizard 18 | 19 | Items: 20 | add $/Project 21 | 22 | ") 23 | 24 | (def ^:const checkin-notes-entry 25 | "----------------------------------------------------------------------------------------------------------------------- 26 | Changeset: 5 27 | User: Ryan Coy 28 | Date: Thursday, July 23, 2015 4:34:31 PM 29 | 30 | Comment: 31 | Created team project folder /Project via the Team Project Creation Wizard 32 | 33 | Items: 34 | add $/Project 35 | 36 | Check-in Notes: 37 | Documentation: 38 | An important new part of our codebase. 39 | ") 40 | 41 | (def ^:const long-comment-entry 42 | "----------------------------------------------------------------------------------------------------------------------- 43 | Changeset: 5 44 | User: Ryan Coy 45 | Date: Thursday, July 23, 2015 4:34:31 PM 46 | 47 | Comment: 48 | Created team project folder /Project via the Team Project Creation Wizard 49 | Gave project a unique and colorful name 50 | 51 | It really is the best project. 52 | ***NO_CI*** 53 | 54 | Items: 55 | add $/Project 56 | 57 | ") 58 | 59 | (def ^:const proxy-checkin-entry 60 | "----------------------------------------------------------------------------------------------------------------------- 61 | Changeset: 5 62 | User: Ryan Coy 63 | Checked in by: build.server 64 | Date: Thursday, July 23, 2015 4:34:31 PM 65 | 66 | Comment: 67 | Created team project folder /Project via the Team Project Creation Wizard 68 | 69 | Items: 70 | add $/Project 71 | 72 | ") 73 | 74 | (def ^:const policy-warning-entry 75 | "----------------------------------------------------------------------------------------------------------------------- 76 | Changeset: 5 77 | User: Ryan Coy 78 | Date: Thursday, July 23, 2015 4:34:31 PM 79 | 80 | Comment: 81 | Created team project folder /Project via the Team Project Creation Wizard 82 | 83 | Items: 84 | add $/Project 85 | 86 | Policy Warnings: 87 | Override Reason: 88 | We don't need no comments 89 | 90 | Not at all 91 | Messages: 92 | Provide a comment for the check-in. 93 | 94 | ...or Else 95 | ") 96 | 97 | (def ^:const en-gb-entry 98 | "----------------------------------------------------------------------------------------------------------------------- 99 | Changeset: 5 100 | User: Ryan Coy 101 | Date: 23 July 2015 16:34:31 102 | 103 | Comment: 104 | Created team project folder /Project via the Team Project Creation Wizard 105 | 106 | Items: 107 | add $/Project 108 | 109 | ") 110 | 111 | (def ^:const entries 112 | "----------------------------------------------------------------------------------------------------------------------- 113 | Changeset: 7 114 | User: Ryan Coy 115 | Date: Thursday, July 23, 2015 4:34:35 PM 116 | 117 | Comment: 118 | Check-in the Lab default template 119 | 120 | Items: 121 | add $/Project/BuildProcessTemplates/LabDefaultTemplate.11.xaml 122 | 123 | ----------------------------------------------------------------------------------------------------------------------- 124 | Changeset: 6 125 | User: Ryan Coy 126 | Date: Thursday, July 23, 2015 4:34:34 PM 127 | 128 | Comment: 129 | Checking in new Team Foundation Build Automation files. 130 | 131 | Items: 132 | add $/Project/BuildProcessTemplates 133 | add $/Project/BuildProcessTemplates/AzureContinuousDeployment.11.xaml 134 | add $/Project/BuildProcessTemplates/DefaultTemplate.11.1.xaml 135 | add $/Project/BuildProcessTemplates/UpgradeTemplate.xaml 136 | 137 | ----------------------------------------------------------------------------------------------------------------------- 138 | Changeset: 5 139 | User: Coy, Ryan 140 | Date: Thursday, July 23, 2015 4:34:31 PM 141 | 142 | Comment: 143 | Created team project folder /Project via the Team Project Creation Wizard 144 | 145 | Items: 146 | add $/Project 147 | 148 | ") 149 | 150 | (defn- parse 151 | [text] 152 | (tfs/parse-read-log text {})) 153 | 154 | (deftest parses-en-us-entry-to-dataset 155 | (is (= (parse en-us-entry) 156 | [{:author "Ryan Coy" 157 | :rev "5" 158 | :date "2015-07-23" 159 | :entity "/Project" 160 | :message "Created team project folder /Project via the Team Project Creation Wizard"}]))) 161 | 162 | (deftest parses-checkin-notes-to-dataset 163 | (is (= (parse checkin-notes-entry) 164 | [{:author "Ryan Coy" 165 | :rev "5" 166 | :date "2015-07-23" 167 | :entity "/Project" 168 | :message "Created team project folder /Project via the Team Project Creation Wizard"}]))) 169 | 170 | (deftest parses-policy-warning-to-dataset 171 | (is (= (parse policy-warning-entry) 172 | [{:author "Ryan Coy" 173 | :rev "5" 174 | :date "2015-07-23" 175 | :entity "/Project" 176 | :message "Created team project folder /Project via the Team Project Creation Wizard"}]))) 177 | 178 | (deftest parses-long-comment-to-dataset 179 | (is (= (parse long-comment-entry) 180 | [{:author "Ryan Coy" 181 | :rev "5" 182 | :date "2015-07-23" 183 | :entity "/Project" 184 | :message "Created team project folder /Project via the Team Project Creation Wizard\nGave project a unique and colorful name\nIt really is the best project.\n***NO_CI***"}]))) 185 | 186 | (deftest parses-proxy-checkin-to-dataset 187 | (is (= (parse proxy-checkin-entry) 188 | [{:author "Ryan Coy" 189 | :rev "5" 190 | :date "2015-07-23" 191 | :entity "/Project" 192 | :message "Created team project folder /Project via the Team Project Creation Wizard"}]))) 193 | 194 | (deftest unparsable-date-throws-exception 195 | (is (thrown? IllegalArgumentException 196 | (parse en-gb-entry)))) 197 | 198 | (deftest parses-multiple-entries-to-dataset 199 | (is (= (parse entries) 200 | [{:author "Ryan Coy" 201 | :rev "7" 202 | :date "2015-07-23" 203 | :entity "/Project/BuildProcessTemplates/LabDefaultTemplate.11.xaml" 204 | :message "Check-in the Lab default template"} 205 | {:author "Ryan Coy" 206 | :rev "6" 207 | :date "2015-07-23" 208 | :entity "/Project/BuildProcessTemplates" 209 | :message "Checking in new Team Foundation Build Automation files."} 210 | {:author "Ryan Coy" 211 | :rev "6" 212 | :date "2015-07-23" 213 | :entity "/Project/BuildProcessTemplates/AzureContinuousDeployment.11.xaml" 214 | :message "Checking in new Team Foundation Build Automation files."} 215 | {:author "Ryan Coy" 216 | :rev "6" 217 | :date "2015-07-23" 218 | :entity "/Project/BuildProcessTemplates/DefaultTemplate.11.1.xaml" 219 | :message "Checking in new Team Foundation Build Automation files."} 220 | {:author "Ryan Coy" 221 | :rev "6" 222 | :date "2015-07-23" 223 | :entity "/Project/BuildProcessTemplates/UpgradeTemplate.xaml" 224 | :message "Checking in new Team Foundation Build Automation files."} 225 | {:author "Coy, Ryan" 226 | :rev "5" 227 | :date "2015-07-23" 228 | :entity "/Project" 229 | :message "Created team project folder /Project via the Team Project Creation Wizard"}]))) 230 | 231 | (deftest parses-empty-log-to-empty-dataset 232 | (is (= (parse "") 233 | []))) 234 | 235 | -------------------------------------------------------------------------------- /test/code_maat/parsers/time_parser_test.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2015 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.parsers.time-parser-test 7 | (:require [code-maat.parsers.time-parser :as p]) 8 | (:use clojure.test)) 9 | 10 | (deftest parses-git-format 11 | (let [parser (p/time-string-converter-from "YYYY-MM-dd") 12 | date-to-parse "2014-12-26"] 13 | (is (= (parser date-to-parse) 14 | date-to-parse)))) 15 | -------------------------------------------------------------------------------- /test/code_maat/tools/test_tools.clj: -------------------------------------------------------------------------------- 1 | ;;; Copyright (C) 2013 Adam Tornhill 2 | ;;; 3 | ;;; Distributed under the GNU General Public License v3.0, 4 | ;;; see http://www.gnu.org/licenses/gpl.html 5 | 6 | (ns code-maat.tools.test-tools 7 | (:require [code-maat.app.app :as app] 8 | [code-maat.test.data-driven :as dd])) 9 | 10 | (defn run-with-str-output [log-file options] 11 | (with-out-str 12 | (app/run log-file options))) 13 | 14 | (defmacro def-data-driven-with-vcs-test 15 | "Encapsulates the common pattern of iterating over a data driven 16 | test providing a vector of [file options] for each item. 17 | The body will execute with the symbols log-file and options bound to 18 | the different options in the test-data." 19 | [name test-data & body] 20 | `(dd/def-dd-test ~name 21 | [~'ddval# ~test-data] 22 | (let [[~'log-file ~'options] ~'ddval#] 23 | ~@body))) 24 | --------------------------------------------------------------------------------