github.com/vlifesystems/rulehunter@v0.0.0-20180501090014-673078aa4a83/html/report_test.go (about) 1 package html 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/lawrencewoodman/dlit" 12 "github.com/vlifesystems/rhkit/assessment" 13 "github.com/vlifesystems/rhkit/description" 14 "github.com/vlifesystems/rulehunter/config" 15 "github.com/vlifesystems/rulehunter/internal/testhelpers" 16 "github.com/vlifesystems/rulehunter/report" 17 ) 18 19 var testDescription = &description.Description{ 20 map[string]*description.Field{ 21 "month": {description.String, nil, nil, 0, 22 map[string]description.Value{ 23 "feb": {dlit.MustNew("feb"), 3}, 24 "may": {dlit.MustNew("may"), 2}, 25 "june": {dlit.MustNew("june"), 9}, 26 }, 27 3, 28 }, 29 "rate": { 30 description.Number, 31 dlit.MustNew(0.3), 32 dlit.MustNew(15.1), 33 3, 34 map[string]description.Value{ 35 "0.3": {dlit.MustNew(0.3), 7}, 36 "7": {dlit.MustNew(7), 2}, 37 "7.3": {dlit.MustNew(7.3), 9}, 38 "9.278": {dlit.MustNew(9.278), 4}, 39 }, 40 4, 41 }, 42 "method": {description.Ignore, nil, nil, 0, 43 map[string]description.Value{}, -1}, 44 }, 45 } 46 47 func TestGenerateReport_single_true_rule(t *testing.T) { 48 report := &report.Report{ 49 Mode: report.Train, 50 Title: "some title", 51 Tags: []string{"bank", "test / fred"}, 52 Category: "testing", 53 Stamp: time.Now(), 54 ExperimentFilename: "somename.yaml", 55 NumRecords: 1000, 56 SortOrder: []assessment.SortOrder{ 57 assessment.SortOrder{ 58 Aggregator: "goalsScore", 59 Direction: assessment.DESCENDING, 60 }, 61 assessment.SortOrder{ 62 Aggregator: "percentMatches", 63 Direction: assessment.ASCENDING, 64 }, 65 }, 66 Aggregators: []report.AggregatorDesc{ 67 report.AggregatorDesc{Name: "numMatches", Kind: "count", Arg: "true()"}, 68 report.AggregatorDesc{ 69 Name: "percentMatches", 70 Kind: "calc", 71 Arg: "roundto(100.0 * numMatches / numRecords, 2)", 72 }, 73 report.AggregatorDesc{Name: "numIncomeGt2", Kind: "count", Arg: "income > 2"}, 74 report.AggregatorDesc{Name: "goalsScore", Kind: "goalsscore", Arg: ""}, 75 }, 76 Description: testDescription, 77 Assessments: []*report.Assessment{ 78 &report.Assessment{ 79 Rule: "true()", 80 Aggregators: []*report.Aggregator{ 81 &report.Aggregator{ 82 Name: "goalsScore", 83 OriginalValue: "0.1", 84 RuleValue: "0.1", 85 Difference: "0", 86 }, 87 &report.Aggregator{ 88 Name: "numIncomeGt2", 89 OriginalValue: "2", 90 RuleValue: "2", 91 Difference: "0", 92 }, 93 &report.Aggregator{ 94 Name: "numMatches", 95 OriginalValue: "142", 96 RuleValue: "142", 97 Difference: "0", 98 }, 99 &report.Aggregator{ 100 Name: "percentMatches", 101 OriginalValue: "42", 102 RuleValue: "42", 103 Difference: "0", 104 }, 105 }, 106 Goals: []*report.Goal{ 107 &report.Goal{ 108 Expr: "numIncomeGt2 == 1", 109 OriginalPassed: false, 110 RulePassed: false, 111 }, 112 &report.Goal{ 113 Expr: "numIncomeGt2 == 2", 114 OriginalPassed: true, 115 RulePassed: true, 116 }, 117 }, 118 }, 119 }, 120 } 121 122 cfgDir := testhelpers.BuildConfigDirs(t, true) 123 defer os.RemoveAll(cfgDir) 124 cfg := &config.Config{ 125 ExperimentsDir: filepath.Join(cfgDir, "experiments"), 126 WWWDir: filepath.Join(cfgDir, "www"), 127 BuildDir: filepath.Join(cfgDir, "build"), 128 MaxNumRecords: 100, 129 MaxNumProcesses: 4, 130 } 131 wantReportURLDir := "reports/category/testing/some-title/train/" 132 133 reportURLDir, err := generateReport(report, cfg) 134 if err != nil { 135 t.Fatalf("generateReport: %s", err) 136 } 137 if reportURLDir != wantReportURLDir { 138 t.Errorf("generateReport - wantReportURLDir: %s, got: %s", 139 wantReportURLDir, reportURLDir) 140 } 141 142 htmlFilename := filepath.Join( 143 cfg.WWWDir, 144 "reports", 145 "category", 146 "testing", 147 "some-title", 148 "train", 149 "index.html", 150 ) 151 // read the whole file at once 152 b, err := ioutil.ReadFile(htmlFilename) 153 if err != nil { 154 t.Fatalf("ReadFile: %s", err) 155 } 156 s := string(b) 157 wantText := "No rule found that improves on the original dataset" 158 dontWantText := "Original Value" 159 if !strings.Contains(s, wantText) { 160 t.Errorf("html file: %s, doesn't contain text \"%s\"", 161 htmlFilename, wantText) 162 } 163 if strings.Contains(s, dontWantText) { 164 t.Errorf("html file: %s, contains text \"%s\"", 165 htmlFilename, dontWantText) 166 } 167 } 168 169 func TestGenerateReport_two_rules(t *testing.T) { 170 report := &report.Report{ 171 Mode: report.Train, 172 Title: "some title", 173 Tags: []string{"bank", "test / fred"}, 174 Category: "testing", 175 Stamp: time.Now(), 176 ExperimentFilename: "somename.yaml", 177 NumRecords: 1000, 178 SortOrder: []assessment.SortOrder{ 179 assessment.SortOrder{ 180 Aggregator: "goalsScore", 181 Direction: assessment.DESCENDING, 182 }, 183 assessment.SortOrder{ 184 Aggregator: "percentMatches", 185 Direction: assessment.ASCENDING, 186 }, 187 }, 188 Aggregators: []report.AggregatorDesc{ 189 report.AggregatorDesc{Name: "numMatches", Kind: "count", Arg: "true()"}, 190 report.AggregatorDesc{ 191 Name: "percentMatches", 192 Kind: "calc", 193 Arg: "roundto(100.0 * numMatches / numRecords, 2)", 194 }, 195 report.AggregatorDesc{Name: "numIncomeGt2", Kind: "count", Arg: "income > 2"}, 196 report.AggregatorDesc{Name: "goalsScore", Kind: "goalsscore", Arg: ""}, 197 }, 198 Description: testDescription, 199 Assessments: []*report.Assessment{ 200 &report.Assessment{ 201 Rule: "rate >= 789.2", 202 Aggregators: []*report.Aggregator{ 203 &report.Aggregator{ 204 Name: "goalsScore", 205 OriginalValue: "0.1", 206 RuleValue: "30.1", 207 Difference: "30", 208 }, 209 &report.Aggregator{ 210 Name: "numIncomeGt2", 211 OriginalValue: "2", 212 RuleValue: "32", 213 Difference: "30", 214 }, 215 &report.Aggregator{ 216 Name: "numMatches", 217 OriginalValue: "142", 218 RuleValue: "3142", 219 Difference: "3000", 220 }, 221 &report.Aggregator{ 222 Name: "percentMatches", 223 OriginalValue: "42", 224 RuleValue: "342", 225 Difference: "300", 226 }, 227 }, 228 Goals: []*report.Goal{ 229 &report.Goal{ 230 Expr: "numIncomeGt2 == 1", 231 OriginalPassed: false, 232 RulePassed: false, 233 }, 234 &report.Goal{ 235 Expr: "numIncomeGt2 == 2", 236 OriginalPassed: true, 237 RulePassed: false, 238 }, 239 }, 240 }, 241 &report.Assessment{ 242 Rule: "true()", 243 Aggregators: []*report.Aggregator{ 244 &report.Aggregator{ 245 Name: "goalsScore", 246 OriginalValue: "0.1", 247 RuleValue: "0.1", 248 Difference: "0", 249 }, 250 &report.Aggregator{ 251 Name: "numIncomeGt2", 252 OriginalValue: "2", 253 RuleValue: "2", 254 Difference: "0", 255 }, 256 &report.Aggregator{ 257 Name: "numMatches", 258 OriginalValue: "142", 259 RuleValue: "142", 260 Difference: "0", 261 }, 262 &report.Aggregator{ 263 Name: "percentMatches", 264 OriginalValue: "42", 265 RuleValue: "42", 266 Difference: "0", 267 }, 268 }, 269 Goals: []*report.Goal{ 270 &report.Goal{ 271 Expr: "numIncomeGt2 == 1", 272 OriginalPassed: false, 273 RulePassed: false, 274 }, 275 &report.Goal{ 276 Expr: "numIncomeGt2 == 2", 277 OriginalPassed: true, 278 RulePassed: true, 279 }, 280 }, 281 }, 282 }, 283 } 284 285 cfgDir := testhelpers.BuildConfigDirs(t, true) 286 defer os.RemoveAll(cfgDir) 287 cfg := &config.Config{ 288 ExperimentsDir: filepath.Join(cfgDir, "experiments"), 289 WWWDir: filepath.Join(cfgDir, "www"), 290 BuildDir: filepath.Join(cfgDir, "build"), 291 MaxNumRecords: 100, 292 MaxNumProcesses: 4, 293 } 294 wantReportURLDir := "reports/category/testing/some-title/train/" 295 296 reportURLDir, err := generateReport(report, cfg) 297 if err != nil { 298 t.Fatalf("generateReport: %s", err) 299 } 300 if reportURLDir != wantReportURLDir { 301 t.Errorf("generateReport - wantReportURLDir: %s, got: %s", 302 wantReportURLDir, reportURLDir) 303 } 304 305 htmlFilename := filepath.Join( 306 cfg.WWWDir, 307 "reports", 308 "category", 309 "testing", 310 "some-title", 311 "train", 312 "index.html", 313 ) 314 // read the whole file at once 315 b, err := ioutil.ReadFile(htmlFilename) 316 if err != nil { 317 t.Fatalf("ReadFile: %s", err) 318 } 319 s := string(b) 320 321 wantText := "Original Value" 322 dontWantText := "No rule found that improves on the original dataset" 323 if !strings.Contains(s, wantText) { 324 t.Errorf("html file: %s, doesn't contain text \"%s\"", 325 htmlFilename, wantText) 326 } 327 if strings.Contains(s, dontWantText) { 328 t.Errorf("html file: %s, contains text \"%s\"", 329 htmlFilename, dontWantText) 330 } 331 } 332 333 func TestGenReportFilename(t *testing.T) { 334 cases := []struct { 335 stamp time.Time 336 mode report.ModeKind 337 category string 338 title string 339 wantFilename string 340 }{ 341 {stamp: time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC), 342 mode: report.Train, 343 category: "", 344 title: "This could be very interesting", 345 wantFilename: filepath.Join( 346 "reports", 347 "nocategory", 348 "this-could-be-very-interesting", 349 "train", 350 "index.html", 351 )}, 352 {stamp: time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC), 353 mode: report.Train, 354 category: "acme or emca", 355 title: "This could be very interesting", 356 wantFilename: filepath.Join( 357 "reports", 358 "category", 359 "acme-or-emca", 360 "this-could-be-very-interesting", 361 "train", 362 "index.html", 363 )}, 364 {stamp: time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC), 365 mode: report.Test, 366 category: "", 367 title: "This could be very interesting", 368 wantFilename: filepath.Join( 369 "reports", 370 "nocategory", 371 "this-could-be-very-interesting", 372 "test", 373 "index.html", 374 )}, 375 {stamp: time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC), 376 mode: report.Test, 377 category: "acme or emca", 378 title: "This could be very interesting", 379 wantFilename: filepath.Join( 380 "reports", 381 "category", 382 "acme-or-emca", 383 "this-could-be-very-interesting", 384 "test", 385 "index.html", 386 )}, 387 } 388 for _, c := range cases { 389 got := genReportFilename(c.mode, c.category, c.title) 390 if got != c.wantFilename { 391 t.Errorf("genReportFilename(%s, %s) got: %s, want: %s", 392 c.stamp, c.title, got, c.wantFilename) 393 } 394 } 395 }