Skip to content

Commit

Permalink
tests: add unit tests for header and template funcs
Browse files Browse the repository at this point in the history
Much of this is being tested with the existing file-based tests in
testdata/*, but this moves us toward a much more targetted and simpler
test structure.
  • Loading branch information
willnorris committed Aug 9, 2021
1 parent ef04bb3 commit 366936e
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 1 deletion.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,5 +334,5 @@ func hasLicense(b []byte) bool {
}
return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright")) ||
bytes.Contains(bytes.ToLower(b[:n]), []byte("mozilla public")) ||
bytes.Contains(bytes.ToLower(b[:n]), []byte("SPDX-License-Identifier"))
bytes.Contains(bytes.ToLower(b[:n]), []byte("spdx-license-identifier"))
}
189 changes: 189 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package main

import (
"html/template"
"io/ioutil"
"os"
"os/exec"
Expand Down Expand Up @@ -206,3 +207,191 @@ func TestMPL(t *testing.T) {
t.Fatalf("%v\n%s", err, out)
}
}

func createTempFile(contents string, pattern string) (*os.File, error) {
f, err := ioutil.TempFile("", pattern)
if err != nil {
return nil, err
}

if err := ioutil.WriteFile(f.Name(), []byte(contents), 0644); err != nil {
return nil, err
}

return f, nil
}

func TestAddLicense(t *testing.T) {
tmpl := template.Must(template.New("").Parse("{{.Holder}}{{.Year}}{{.SPDXID}}"))
data := licenseData{Holder: "H", Year: "Y", SPDXID: "S"}

tests := []struct {
contents string
wantContents string
wantUpdated bool
}{
{"", "// HYS\n\n", true},
{"content", "// HYS\n\ncontent", true},

// various headers that should be left intact. Many don't make
// sense for our temp file extension, but that doesn't matter.
{"#!/bin/bash\ncontent", "#!/bin/bash\n// HYS\n\ncontent", true},
{"<?xml version='1.0'?>\ncontent", "<?xml version='1.0'?>\n// HYS\n\ncontent", true},
{"<!doctype html>\ncontent", "<!doctype html>\n// HYS\n\ncontent", true},
{"<!DOCTYPE HTML>\ncontent", "<!DOCTYPE HTML>\n// HYS\n\ncontent", true},
{"# encoding: UTF-8\ncontent", "# encoding: UTF-8\n// HYS\n\ncontent", true},
{"# frozen_string_literal: true\ncontent", "# frozen_string_literal: true\n// HYS\n\ncontent", true},
{"<?php\ncontent", "<?php\n// HYS\n\ncontent", true},

// ensure files with existing license or generated files are
// skipped. No need to test all permutations of these, since
// there are specific tests below.
{"// Copyright 2000 Acme\ncontent", "// Copyright 2000 Acme\ncontent", false},
{"// Code generated by go generate; DO NOT EDIT.\ncontent", "// Code generated by go generate; DO NOT EDIT.\ncontent", false},
}

for _, tt := range tests {
// create temp file with contents
f, err := createTempFile(tt.contents, "*.go")
if err != nil {
t.Error(err)
}
fi, err := f.Stat()
if err != nil {
t.Error(err)
}

// run addlicense
updated, err := addLicense(f.Name(), fi.Mode(), tmpl, data)
if err != nil {
t.Error(err)
}

// check results
if updated != tt.wantUpdated {
t.Errorf("addLicense with contents %q returned updated: %t, want %t", tt.contents, updated, tt.wantUpdated)
}
gotContents, err := ioutil.ReadFile(f.Name())
if err != nil {
t.Error(err)
}
if got := string(gotContents); got != tt.wantContents {
t.Errorf("addLicense with contents %q returned contents: %q, want %q", tt.contents, got, tt.wantContents)
}

// if all tests passed, cleanup temp file
if !t.Failed() {
_ = os.Remove(f.Name())
}
}
}

// Test that license headers are added using the appropriate prefix for
// different filenames and extensions.
func TestLicenseHeader(t *testing.T) {
tpl := template.Must(template.New("").Parse("{{.Holder}}{{.Year}}{{.SPDXID}}"))
data := licenseData{Holder: "H", Year: "Y", SPDXID: "S"}

tests := []struct {
paths []string // paths passed to licenseHeader
want string // expected result of executing template
}{
{
[]string{"f.unknown"},
"",
},
{
[]string{"f.c", "f.h", "f.gv"},
"/*\n * HYS\n */\n\n",
},
{
[]string{"f.js", "f.mjs", "f.cjs", "f.jsx", "f.tsx", "f.css", "f.scss", "f.sass", "f.tf", "f.ts"},
"/**\n * HYS\n */\n\n",
},
{
[]string{"f.cc", "f.cpp", "f.cs", "f.go", "f.hh", "f.hpp", "f.java", "f.m", "f.mm", "f.proto",
"f.rs", "f.scala", "f.swift", "f.dart", "f.groovy", "f.kt", "f.kts", "f.v", "f.sv", "f.php"},
"// HYS\n\n",
},
{
[]string{"f.py", "f.sh", "f.yaml", "f.yml", "f.dockerfile", "dockerfile", "f.rb", "gemfile", "f.tcl", "f.bzl"},
"# HYS\n\n",
},
{
[]string{"f.el", "f.lisp"},
";; HYS\n\n",
},
{
[]string{"f.erl"},
"% HYS\n\n",
},
{
[]string{"f.hs", "f.sql", "f.sdl"},
"-- HYS\n\n",
},
{
[]string{"f.html", "f.xml", "f.vue"},
"<!--\n HYS\n-->\n\n",
},
{
[]string{"f.ml", "f.mli", "f.mll", "f.mly"},
"(**\n HYS\n*)\n\n",
},
}

for _, tt := range tests {
for _, path := range tt.paths {
header, _ := licenseHeader(path, tpl, data)
if got := string(header); got != tt.want {
t.Errorf("licenseHeader(%q) returned: %q, want: %q", path, got, tt.want)
}
}
}
}

// Test that generated files are properly recognized.
func TestIsGenerated(t *testing.T) {
tests := []struct {
content string
want bool
}{
{"", false},
{"Generated", false},
{"// Code generated by go generate; DO NOT EDIT.", true},
{"/*\n* Code generated by go generate; DO NOT EDIT.\n*/\n", true},
{"DO NOT EDIT! Replaced on runs of cargo-raze", true},
}

for _, tt := range tests {
b := []byte(tt.content)
if got := isGenerated(b); got != tt.want {
t.Errorf("isGenerated(%q) returned %v, want %v", tt.content, got, tt.want)
}
}
}

// Test that existing license headers are identified.
func TestHasLicense(t *testing.T) {
tests := []struct {
content string
want bool
}{
{"", false},
{"This is my license", false},
{"This code is released into the public domain.", false},
{"SPDX: MIT", false},

{"Copyright 2000", true},
{"CoPyRiGhT 2000", true},
{"Subject to the terms of the Mozilla Public License", true},
{"SPDX-License-Identifier: MIT", true},
{"spdx-license-identifier: MIT", true},
}

for _, tt := range tests {
b := []byte(tt.content)
if got := hasLicense(b); got != tt.want {
t.Errorf("hasLicense(%q) returned %v, want %v", tt.content, got, tt.want)
}
}
}
48 changes: 48 additions & 0 deletions tmpl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,51 @@ func TestFetchTemplate(t *testing.T) {
})
}
}

func TestExecuteTemplate(t *testing.T) {
tests := []struct {
template string
data licenseData
top, mid, bot string
want string
}{
{
"",
licenseData{},
"", "", "",
"\n",
},
{
"{{.Holder}}{{.Year}}{{.SPDXID}}",
licenseData{Holder: "H", Year: "Y", SPDXID: "S"},
"", "", "",
"HYS\n\n",
},
{
"{{.Holder}}{{.Year}}{{.SPDXID}}",
licenseData{Holder: "H", Year: "Y", SPDXID: "S"},
"", "// ", "",
"// HYS\n\n",
},
{
"{{.Holder}}{{.Year}}{{.SPDXID}}",
licenseData{Holder: "H", Year: "Y", SPDXID: "S"},
"/*", " * ", "*/",
"/*\n * HYS\n*/\n\n",
},
}

for _, tt := range tests {
tpl, err := template.New("").Parse(tt.template)
if err != nil {
t.Errorf("error parsing template: %v", err)
}
got, err := executeTemplate(tpl, tt.data, tt.top, tt.mid, tt.bot)
if err != nil {
t.Errorf("executeTemplate(%q, %v, %q, %q, %q) returned error: %v", tt.template, tt.data, tt.top, tt.mid, tt.bot, err)
}
if string(got) != tt.want {
t.Errorf("executeTemplate(%q, %v, %q, %q, %q) returned %q, want: %q", tt.template, tt.data, tt.top, tt.mid, tt.bot, string(got), tt.want)
}
}
}

0 comments on commit 366936e

Please sign in to comment.