Repository: googleapis/api-linter
Pull request #330 (2019-11-09 20:00:19)

Added AIP number in rule register function.
Rule registering now requires a valid AIP number. It removes the redundant tests in each rule package testing if a rule is in the right group. 

mingzhi: This PR removes >500 lines of duplicated codes :)
lukesneeringer: I think this PR would drop coverage in the rule modules below 100%, would it not?
mingzhi: > I think this PR would drop coverage in the rule modules below 100%, would it not?

I just realized we never try to catch errors when registering a rule (see [this example](https://github.com/googleapis/api-linter/blob/dc122848d11993e27560699426456f313760cc19/rules/aip0122/aip0122.go#L25)). In other words, `AddRules` must returns an error or panic.
mingzhi: > I think this PR would drop coverage in the rule modules below 100%, would it not?

The coverage is back to 100%. But we need to fix the meta_linter.
mingzhi: ```
ok  	github.com/googleapis/api-linter/rules	0.220s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0122	0.227s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0126	0.254s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0131	0.233s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0132	0.203s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0133	0.316s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0134	0.240s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0135	0.234s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0136	0.257s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0140	0.294s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0141	0.295s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0142	0.301s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0143	0.190s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0151	0.208s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0158	0.209s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0191	0.243s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0192	0.448s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/aip0203	0.315s	coverage: 97.7% of statements
ok  	github.com/googleapis/api-linter/rules/aip0231	0.311s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/internal/data	0.065s	coverage: [no statements]
ok  	github.com/googleapis/api-linter/rules/internal/testutils	0.164s	coverage: 100.0% of statements
ok  	github.com/googleapis/api-linter/rules/internal/utils	0.164s	coverage: 100.0% of statements
```
mingzhi: > I think that the `AddRules` pattern may have been an error. We can always change it later, but this is an incremental improvement.

+1

Patch:
diff --git a/.circleci/meta-linter/rules_registered.go b/.circleci/meta-linter/rules_registered.go
index c0d299f..b17272e 100644
--- a/.circleci/meta-linter/rules_registered.go
+++ b/.circleci/meta-linter/rules_registered.go
@@ -77,7 +77,7 @@ func checkRulesRegistered() (errors []error) {
 	contents := string(contentsBytes)
 	for aip, rules := range allRules {
 		// Make sure the AIP's package's rules are registered in coreRules in rules.go.
-		if !strings.Contains(contents, fmt.Sprintf("aip%04d.AddRules(coreRules)", aip)) {
+		if !strings.Contains(contents, fmt.Sprintf("aip%04d.AddRules", aip)) {
 			errors = append(errors, fmt.Errorf("rules.go does not call AllRules for for AIP-%d", aip))
 		}
 
diff --git a/cmd/api-linter/main.go b/cmd/api-linter/main.go
index 7fa3d60..93ed97e 100644
--- a/cmd/api-linter/main.go
+++ b/cmd/api-linter/main.go
@@ -21,12 +21,18 @@ import (
 	"os"
 
 	"github.com/googleapis/api-linter/lint"
-	core "github.com/googleapis/api-linter/rules"
+	"github.com/googleapis/api-linter/rules"
 )
 
-var globalRules = core.Rules()
+var globalRules = lint.NewRuleRegistry()
 var globalConfigs = defaultConfigs()
 
+func init() {
+	if err := rules.Add(globalRules); err != nil {
+		log.Fatalf("error when registering rules: %v", err)
+	}
+}
+
 func main() {
 	if err := runCLI(os.Args[1:]); err != nil {
 		log.Fatalln(err)
diff --git a/lint/rule_registry.go b/lint/rule_registry.go
index 995688e..a660855 100644
--- a/lint/rule_registry.go
+++ b/lint/rule_registry.go
@@ -15,6 +15,7 @@
 package lint
 
 import (
+	"errors"
 	"fmt"
 )
 
@@ -30,16 +31,25 @@ func (r RuleRegistry) Copy() RuleRegistry {
 	return n
 }
 
-// Register registers the list of rules.
+var errInvalidRuleName = errors.New("not a valid rule name")
+var errInvalidRuleGroup = errors.New("invalid rule group")
+var errDuplicatedRuleName = errors.New("duplicate rule name")
+
+// Register registers the list of rules of the same AIP.
 // Return an error if any of the rules is found duplicate in the registry.
-func (r RuleRegistry) Register(rules ...ProtoRule) error {
+func (r RuleRegistry) Register(aip int, rules ...ProtoRule) error {
+	rulePrefix := getRuleGroup(aip) + nameSeparator + fmt.Sprintf("%04d", aip)
 	for _, rl := range rules {
 		if !rl.GetName().IsValid() {
-			return fmt.Errorf("%q is not a valid RuleName", rl.GetName())
+			return errInvalidRuleName
+		}
+
+		if !rl.GetName().HasPrefix(rulePrefix) {
+			return errInvalidRuleGroup
 		}
 
 		if _, found := r[rl.GetName()]; found {
-			return fmt.Errorf("duplicate rule name %q", rl.GetName())
+			return errDuplicatedRuleName
 		}
 
 		r[rl.GetName()] = rl
@@ -47,18 +57,7 @@ func (r RuleRegistry) Register(rules ...ProtoRule) error {
 	return nil
 }
 
-// All returns all rules.
-func (r RuleRegistry) All() []ProtoRule {
-	rules := make([]ProtoRule, 0, len(r))
-	for _, r1 := range r {
-		rules = append(rules, r1)
-	}
-	return rules
-}
-
-// NewRuleRegistry returns a rule registry initialized with the given set of rules.
-func NewRuleRegistry(rules ...ProtoRule) (RuleRegistry, error) {
-	r := make(RuleRegistry, len(rules))
-	err := r.Register(rules...)
-	return r, err
+// NewRuleRegistry creates a new rule registry.
+func NewRuleRegistry() RuleRegistry {
+	return make(RuleRegistry)
 }
diff --git a/rules/aip0122/aip0122.go b/rules/aip0122/aip0122.go
index ee40e57..a7a40c6 100644
--- a/rules/aip0122/aip0122.go
+++ b/rules/aip0122/aip0122.go
@@ -21,8 +21,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		122,
 		httpURICase,
 	)
 }
diff --git a/rules/aip0126/aip0126.go b/rules/aip0126/aip0126.go
index c820b16..45eeaae 100644
--- a/rules/aip0126/aip0126.go
+++ b/rules/aip0126/aip0126.go
@@ -18,8 +18,9 @@ package aip0126
 import "github.com/googleapis/api-linter/lint"
 
 // AddRules adds all of the AIP-126 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		126,
 		enumValueUpperSnakeCase,
 		unspecified,
 	)
diff --git a/rules/aip0131/aip0131.go b/rules/aip0131/aip0131.go
index fedf2bb..847d2fe 100644
--- a/rules/aip0131/aip0131.go
+++ b/rules/aip0131/aip0131.go
@@ -24,8 +24,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		131,
 		httpBody,
 		httpMethod,
 		httpNameField,
diff --git a/rules/aip0132/aip0132.go b/rules/aip0132/aip0132.go
index 7d010a3..fe5eefc 100644
--- a/rules/aip0132/aip0132.go
+++ b/rules/aip0132/aip0132.go
@@ -23,8 +23,9 @@ import (
 )
 
 // AddRules adds all of the AIP-132 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		132,
 		requestFieldTypes,
 		requestMessageName,
 		responseMessageName,
diff --git a/rules/aip0133/aip0133.go b/rules/aip0133/aip0133.go
index a376d9d..c2d76d2 100644
--- a/rules/aip0133/aip0133.go
+++ b/rules/aip0133/aip0133.go
@@ -25,8 +25,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		133,
 		httpBody,
 		httpURIField,
 		httpMethod,
diff --git a/rules/aip0134/aip0134.go b/rules/aip0134/aip0134.go
index dae974e..cea0729 100644
--- a/rules/aip0134/aip0134.go
+++ b/rules/aip0134/aip0134.go
@@ -26,8 +26,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		134,
 		httpBody,
 		httpMethod,
 		httpNameField,
diff --git a/rules/aip0135/aip0135.go b/rules/aip0135/aip0135.go
index a3e2acf..a2f8a6c 100644
--- a/rules/aip0135/aip0135.go
+++ b/rules/aip0135/aip0135.go
@@ -24,8 +24,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		135,
 		httpBody,
 		httpMethod,
 		httpNameField,
diff --git a/rules/aip0136/aip0136.go b/rules/aip0136/aip0136.go
index 45bbbe8..352ac4e 100644
--- a/rules/aip0136/aip0136.go
+++ b/rules/aip0136/aip0136.go
@@ -24,8 +24,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		136,
 		httpBody,
 		httpMethod,
 		noPrepositions,
diff --git a/rules/aip0140/aip0140.go b/rules/aip0140/aip0140.go
index c4c14f3..8081a86 100644
--- a/rules/aip0140/aip0140.go
+++ b/rules/aip0140/aip0140.go
@@ -25,8 +25,9 @@ import (
 )
 
 // AddRules adds all of the AIP-140 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		140,
 		abbreviations,
 		base64,
 		lowerSnake,
diff --git a/rules/aip0141/aip0141.go b/rules/aip0141/aip0141.go
index 2f7b24f..4987077 100644
--- a/rules/aip0141/aip0141.go
+++ b/rules/aip0141/aip0141.go
@@ -20,8 +20,9 @@ import (
 )
 
 // AddRules adds all of the AIP-141 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		141,
 		count,
 		forbiddenTypes,
 	)
diff --git a/rules/aip0142/aip0142.go b/rules/aip0142/aip0142.go
index c772e5c..92fa95d 100644
--- a/rules/aip0142/aip0142.go
+++ b/rules/aip0142/aip0142.go
@@ -20,8 +20,9 @@ import (
 )
 
 // AddRules adds all of the AIP-142 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		142,
 		fieldNames,
 		fieldType,
 	)
diff --git a/rules/aip0143/aip0143.go b/rules/aip0143/aip0143.go
index ef1170f..75bd518 100644
--- a/rules/aip0143/aip0143.go
+++ b/rules/aip0143/aip0143.go
@@ -20,8 +20,9 @@ import (
 )
 
 // AddRules adds all of the AIP-141 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		143,
 		fieldNames,
 		fieldTypes,
 	)
diff --git a/rules/aip0151/aip0151.go b/rules/aip0151/aip0151.go
index 41c56c6..3e2665d 100644
--- a/rules/aip0151/aip0151.go
+++ b/rules/aip0151/aip0151.go
@@ -22,8 +22,9 @@ import (
 )
 
 // AddRules adds all of the AIP-151 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		151,
 		lroAnnotationExists,
 		lroDefinedInFile,
 		lroMetadata,
diff --git a/rules/aip0158/aip0158.go b/rules/aip0158/aip0158.go
index ee4d0c9..dbde76f 100644
--- a/rules/aip0158/aip0158.go
+++ b/rules/aip0158/aip0158.go
@@ -23,8 +23,9 @@ import (
 )
 
 // AddRules adds all of the AIP-158 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		158,
 		requestPaginationPageSize,
 		requestPaginationPageToken,
 		responsePaginationNextPageToken,
diff --git a/rules/aip0191/aip0191.go b/rules/aip0191/aip0191.go
index 3296b41..a6dca94 100644
--- a/rules/aip0191/aip0191.go
+++ b/rules/aip0191/aip0191.go
@@ -22,8 +22,9 @@ import (
 )
 
 // AddRules adds all of the AIP-191 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		191,
 		filename,
 		syntax,
 	)
diff --git a/rules/aip0192/aip0192.go b/rules/aip0192/aip0192.go
index a415d26..691b886 100644
--- a/rules/aip0192/aip0192.go
+++ b/rules/aip0192/aip0192.go
@@ -22,8 +22,9 @@ import (
 )
 
 // AddRules adds all of the AIP-192 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		192,
 		hasComments,
 		onlyLeadingComments,
 	)
diff --git a/rules/aip0203/aip0203.go b/rules/aip0203/aip0203.go
index 2d9a6b0..d661462 100644
--- a/rules/aip0203/aip0203.go
+++ b/rules/aip0203/aip0203.go
@@ -24,8 +24,9 @@ import (
 )
 
 // AddRules adds all of the AIP-203 rules to the provided registry.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		203,
 		inputOnly,
 		immutable,
 		optional,
diff --git a/rules/aip0231/aip0231.go b/rules/aip0231/aip0231.go
index b7bb28c..dadb5f9 100644
--- a/rules/aip0231/aip0231.go
+++ b/rules/aip0231/aip0231.go
@@ -24,8 +24,9 @@ import (
 
 // AddRules accepts a register function and registers each of
 // this AIP's rules to it.
-func AddRules(r lint.RuleRegistry) {
-	r.Register(
+func AddRules(r lint.RuleRegistry) error {
+	return r.Register(
+		231,
 		pluralMethodResourceName,
 		inputName,
 		outputName,
diff --git a/rules/rules.go b/rules/rules.go
index 53dc187..dc558fd 100644
--- a/rules/rules.go
+++ b/rules/rules.go
@@ -71,30 +71,39 @@ import (
 	"github.com/googleapis/api-linter/rules/aip0231"
 )
 
-func init() {
-	aip0122.AddRules(coreRules)
-	aip0126.AddRules(coreRules)
-	aip0131.AddRules(coreRules)
-	aip0132.AddRules(coreRules)
-	aip0133.AddRules(coreRules)
-	aip0134.AddRules(coreRules)
-	aip0135.AddRules(coreRules)
-	aip0136.AddRules(coreRules)
-	aip0140.AddRules(coreRules)
-	aip0141.AddRules(coreRules)
-	aip0142.AddRules(coreRules)
-	aip0143.AddRules(coreRules)
-	aip0151.AddRules(coreRules)
-	aip0158.AddRules(coreRules)
-	aip0191.AddRules(coreRules)
-	aip0192.AddRules(coreRules)
-	aip0203.AddRules(coreRules)
-	aip0231.AddRules(coreRules)
+type addRulesFuncType func(lint.RuleRegistry) error
+
+var aipAddRulesFuncs = []addRulesFuncType{
+	aip0122.AddRules,
+	aip0126.AddRules,
+	aip0131.AddRules,
+	aip0132.AddRules,
+	aip0133.AddRules,
+	aip0134.AddRules,
+	aip0135.AddRules,
+	aip0136.AddRules,
+	aip0140.AddRules,
+	aip0141.AddRules,
+	aip0142.AddRules,
+	aip0143.AddRules,
+	aip0151.AddRules,
+	aip0158.AddRules,
+	aip0191.AddRules,
+	aip0192.AddRules,
+	aip0203.AddRules,
+	aip0231.AddRules,
 }
 
-var coreRules, _ = lint.NewRuleRegistry()
+// Add all rules to the given registry.
+func Add(r lint.RuleRegistry) error {
+	return addAIPRules(r, aipAddRulesFuncs)
+}
 
-// Rules returns all rules registered in this package.
-func Rules() lint.RuleRegistry {
-	return coreRules.Copy()
+func addAIPRules(r lint.RuleRegistry, addRulesFuncs []addRulesFuncType) error {
+	for _, addRules := range addRulesFuncs {
+		if err := addRules(r); err != nil {
+			return err
+		}
+	}
+	return nil
 }


Test patch:
diff --git a/lint/lint_test.go b/lint/lint_test.go
index d1a2668..7f644d6 100644
--- a/lint/lint_test.go
+++ b/lint/lint_test.go
@@ -31,12 +31,12 @@ func TestLinter_run(t *testing.T) {
 	}
 	defaultConfigs := Configs{}
 
-	testRuleID := "test::rule1"
+	testRuleName := NewRuleName(111, "test-rule")
 	ruleProblems := []Problem{{
 		Message:    "rule1_problem",
 		Descriptor: fd,
 		category:   "",
-		RuleID:     RuleName(testRuleID),
+		RuleID:     testRuleName,
 	}}
 
 	tests := []struct {
@@ -70,7 +70,7 @@ func TestLinter_run(t *testing.T) {
 			append(
 				defaultConfigs,
 				Config{
-					DisabledRules: []string{testRuleID},
+					DisabledRules: []string{string(testRuleName)},
 				},
 			),
 			[]Problem{},
@@ -79,8 +79,9 @@ func TestLinter_run(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.testName, func(t *testing.T) {
-			rules, err := NewRuleRegistry(&FileRule{
-				Name: "test::rule1",
+			rules := NewRuleRegistry()
+			err := rules.Register(111, &FileRule{
+				Name: NewRuleName(111, "test-rule"),
 				LintFile: func(f *desc.FileDescriptor) []Problem {
 					return test.problems
 				},
@@ -107,6 +108,8 @@ func TestLinter_LintProtos_RulePanics(t *testing.T) {
 		t.Fatalf("Failed to build the file descriptor.")
 	}
 
+	testAIP := 111
+
 	tests := []struct {
 		testName string
 		rule     ProtoRule
@@ -114,7 +117,7 @@ func TestLinter_LintProtos_RulePanics(t *testing.T) {
 		{
 			testName: "Panic",
 			rule: &FileRule{
-				Name: "panic",
+				Name: NewRuleName(testAIP, "panic"),
 				LintFile: func(_ *desc.FileDescriptor) []Problem {
 					panic("panic")
 				},
@@ -123,7 +126,7 @@ func TestLinter_LintProtos_RulePanics(t *testing.T) {
 		{
 			testName: "PanicError",
 			rule: &FileRule{
-				Name: "panic-error",
+				Name: NewRuleName(testAIP, "panic-error"),
 				LintFile: func(_ *desc.FileDescriptor) []Problem {
 					panic(fmt.Errorf("panic"))
 				},
@@ -133,13 +136,14 @@ func TestLinter_LintProtos_RulePanics(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.testName, func(t *testing.T) {
-			r, err := NewRuleRegistry(test.rule)
+			rules := NewRuleRegistry()
+			err := rules.Register(testAIP, test.rule)
 			if err != nil {
 				t.Fatalf("Failed to create Rules: %q", err)
 			}
 
 			// Instantiate a linter with the given rule.
-			l := New(r, nil)
+			l := New(rules, nil)
 
 			_, err = l.LintProtos(fd)
 			if err == nil || !strings.Contains(err.Error(), "panic") {
diff --git a/lint/rule_registry_test.go b/lint/rule_registry_test.go
index ddd6cd6..3d881b6 100644
--- a/lint/rule_registry_test.go
+++ b/lint/rule_registry_test.go
@@ -19,31 +19,49 @@ import (
 )
 
 func TestRuleRegistryRegister(t *testing.T) {
-	r1 := &FileRule{Name: "a"}
-	r2 := &FileRule{Name: "b"}
-
-	rules, _ := NewRuleRegistry()
-	if err := rules.Register(r1, r2); err != nil {
-		t.Errorf("Register: return error %v, but want nil", err)
-	}
-
-	numRegistered := len(rules.All())
-	if numRegistered != 2 {
-		t.Errorf("Register: got %d rules, but want %d", numRegistered, 2)
-	}
-}
-
-func TestRulesRegister_Duplicate(t *testing.T) {
-	r1 := &FileRule{Name: "a"}
-	r2 := &FileRule{Name: "a"}
-
-	rules, _ := NewRuleRegistry()
-	if err := rules.Register(r1, r2); err == nil {
-		t.Errorf("Register with duplicate name")
+	tests := []struct {
+		name      string
+		aip       int
+		ruleNames []RuleName
+		err       error
+	}{
+		{
+			name:      "Registered_Okay",
+			aip:       111,
+			ruleNames: []RuleName{NewRuleName(111, "a"), NewRuleName(111, "b")},
+			err:       nil,
+		},
+		{
+			name:      "InvalidRuleName",
+			aip:       111,
+			ruleNames: []RuleName{NewRuleName(111, "")},
+			err:       errInvalidRuleName,
+		},
+		{
+			name:      "InvalidRuleGroup",
+			aip:       111,
+			ruleNames: []RuleName{NewRuleName(100, "a")},
+			err:       errInvalidRuleGroup,
+		},
+		{
+			name:      "Duplicated",
+			aip:       111,
+			ruleNames: []RuleName{NewRuleName(111, "a"), NewRuleName(111, "a")},
+			err:       errDuplicatedRuleName,
+		},
 	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			rules := []ProtoRule{}
+			for _, name := range test.ruleNames {
+				rules = append(rules, &FileRule{Name: name})
+			}
 
-	numRegistered := len(rules.All())
-	if numRegistered != 1 {
-		t.Errorf("Register: got %d rules, but want %d", numRegistered, 1)
+			registry := NewRuleRegistry()
+			err := registry.Register(test.aip, rules...)
+			if err != test.err {
+				t.Errorf("Register(): got %v, but want %v", err, test.err)
+			}
+		})
 	}
 }
diff --git a/rules/aip0122/aip0122_test.go b/rules/aip0122/aip0122_test.go
deleted file mode 100644
index ae83714..0000000
--- a/rules/aip0122/aip0122_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package aip0122
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/googleapis/api-linter/lint"
-)
-
-func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0122") {
-			t.Errorf("Rule %s is not namespaced to core::0122.", ruleName)
-		}
-	}
-}
diff --git a/rules/aip0122/api0122_test.go b/rules/aip0122/api0122_test.go
new file mode 100644
index 0000000..555f316
--- /dev/null
+++ b/rules/aip0122/api0122_test.go
@@ -0,0 +1,13 @@
+package aip0122
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/lint"
+)
+
+func TestAddRules(t *testing.T) {
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
+	}
+}
diff --git a/rules/aip0126/aip0126_test.go b/rules/aip0126/aip0126_test.go
new file mode 100644
index 0000000..b68eec5
--- /dev/null
+++ b/rules/aip0126/aip0126_test.go
@@ -0,0 +1,13 @@
+package aip0126
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/lint"
+)
+
+func TestAddRules(t *testing.T) {
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
+	}
+}
diff --git a/rules/aip0126/api0126_test.go b/rules/aip0126/api0126_test.go
deleted file mode 100644
index de242fd..0000000
--- a/rules/aip0126/api0126_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package aip0126
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/googleapis/api-linter/lint"
-)
-
-func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0126") {
-			t.Errorf("Rule %s is not namespaced to core::0126.", ruleName)
-		}
-	}
-}
diff --git a/rules/aip0131/aip0131_test.go b/rules/aip0131/aip0131_test.go
index f3e09c5..82b7f82 100644
--- a/rules/aip0131/aip0131_test.go
+++ b/rules/aip0131/aip0131_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0131
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0131") {
-			t.Errorf("Rule %s is not namespaced to core::0131.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0132/aip0132_test.go b/rules/aip0132/aip0132_test.go
index c964c8c..8a07c52 100644
--- a/rules/aip0132/aip0132_test.go
+++ b/rules/aip0132/aip0132_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0132
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0132") {
-			t.Errorf("Rule %s is not namespaced to core::0132.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0133/aip0133_test.go b/rules/aip0133/aip0133_test.go
index 4ea4a7c..26f6c08 100644
--- a/rules/aip0133/aip0133_test.go
+++ b/rules/aip0133/aip0133_test.go
@@ -1,38 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0133
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
-	"github.com/jhump/protoreflect/desc/builder"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0133") {
-			t.Errorf("Rule %s is not namespaced to core::0133.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
-
-type field struct {
-	fieldName string
-	fieldType *builder.FieldType
-}
diff --git a/rules/aip0133/request_unknown_fields_test.go b/rules/aip0133/request_unknown_fields_test.go
index c02cb24..6041ffa 100644
--- a/rules/aip0133/request_unknown_fields_test.go
+++ b/rules/aip0133/request_unknown_fields_test.go
@@ -22,6 +22,11 @@ import (
 	"github.com/jhump/protoreflect/desc/builder"
 )
 
+type field struct {
+	fieldName string
+	fieldType *builder.FieldType
+}
+
 func TestUnknownFields(t *testing.T) {
 	// Set up the testing permutations.
 	tests := []struct {
diff --git a/rules/aip0134/aip0134_test.go b/rules/aip0134/aip0134_test.go
index c88534b..1c80ffb 100644
--- a/rules/aip0134/aip0134_test.go
+++ b/rules/aip0134/aip0134_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0134
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0134") {
-			t.Errorf("Rule %s is not namespaced to core::0134.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0135/aip0135_test.go b/rules/aip0135/aip0135_test.go
index 3136705..79cf804 100644
--- a/rules/aip0135/aip0135_test.go
+++ b/rules/aip0135/aip0135_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0135
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0135") {
-			t.Errorf("Rule %s is not namespaced to core::0135.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0136/aip0136_test.go b/rules/aip0136/aip0136_test.go
index 7ead22e..81f6962 100644
--- a/rules/aip0136/aip0136_test.go
+++ b/rules/aip0136/aip0136_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0136
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0136") {
-			t.Errorf("Rule %s is not namespaced to core::0136.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0140/aip0140_test.go b/rules/aip0140/aip0140_test.go
index 6447a76..d48ace5 100644
--- a/rules/aip0140/aip0140_test.go
+++ b/rules/aip0140/aip0140_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0140
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0140") {
-			t.Errorf("Rule %s is not namespaced to core::0140.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0141/aip0141_test.go b/rules/aip0141/aip0141_test.go
index afe4cb1..bbec27d 100644
--- a/rules/aip0141/aip0141_test.go
+++ b/rules/aip0141/aip0141_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0141
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0141") {
-			t.Errorf("Rule %s is not namespaced to core::0141.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0142/aip0142_test.go b/rules/aip0142/aip0142_test.go
index d1d1d7f..070b680 100644
--- a/rules/aip0142/aip0142_test.go
+++ b/rules/aip0142/aip0142_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0142
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0142") {
-			t.Errorf("Rule %s is not namespaced to core::0142.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0143/aip0143_test.go b/rules/aip0143/aip0143_test.go
index ceb49b8..034c08c 100644
--- a/rules/aip0143/aip0143_test.go
+++ b/rules/aip0143/aip0143_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0143
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0143") {
-			t.Errorf("Rule %s is not namespaced to core::0143.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0151/aip0151_test.go b/rules/aip0151/aip0151_test.go
index 34bbbc8..0a059b4 100644
--- a/rules/aip0151/aip0151_test.go
+++ b/rules/aip0151/aip0151_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0151
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0151") {
-			t.Errorf("Rule %s is not namespaced to core::0151.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0158/aip0158_test.go b/rules/aip0158/aip0158_test.go
index 620abbd..d079845 100644
--- a/rules/aip0158/aip0158_test.go
+++ b/rules/aip0158/aip0158_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0158
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0158") {
-			t.Errorf("Rule %s is not namespaced to core::0158.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0191/aip0191_test.go b/rules/aip0191/aip0191_test.go
index f3badee..36e6c09 100644
--- a/rules/aip0191/aip0191_test.go
+++ b/rules/aip0191/aip0191_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0191
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0191") {
-			t.Errorf("Rule %s is not namespaced to core::0191.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0192/aip0192_test.go b/rules/aip0192/aip0192_test.go
index e5d8ffb..674fd2f 100644
--- a/rules/aip0192/aip0192_test.go
+++ b/rules/aip0192/aip0192_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0192
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0192") {
-			t.Errorf("Rule %s is not namespaced to core::0192.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0203/aip0203_test.go b/rules/aip0203/aip0203_test.go
index 93cd21e..fdc75ed 100644
--- a/rules/aip0203/aip0203_test.go
+++ b/rules/aip0203/aip0203_test.go
@@ -1,32 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0203
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0203") {
-			t.Errorf("Rule %s is not namespaced to core::0203.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
diff --git a/rules/aip0231/aip0231_test.go b/rules/aip0231/aip0231_test.go
index 5071041..ac537b8 100644
--- a/rules/aip0231/aip0231_test.go
+++ b/rules/aip0231/aip0231_test.go
@@ -1,38 +1,13 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package aip0231
 
 import (
-	"strings"
 	"testing"
 
 	"github.com/googleapis/api-linter/lint"
-	"github.com/jhump/protoreflect/desc/builder"
 )
 
 func TestAddRules(t *testing.T) {
-	rules := make(lint.RuleRegistry)
-	AddRules(rules)
-	for ruleName := range rules {
-		if !strings.HasPrefix(string(ruleName), "core::0231") {
-			t.Errorf("Rule %s is not namespaced to core::0231.", ruleName)
-		}
+	if err := AddRules(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("AddRules got an error: %v", err)
 	}
 }
-
-type field struct {
-	fieldName string
-	fieldType *builder.FieldType
-}
diff --git a/rules/aip0231/request_parent_field_test.go b/rules/aip0231/request_parent_field_test.go
index ba394da..916f12f 100644
--- a/rules/aip0231/request_parent_field_test.go
+++ b/rules/aip0231/request_parent_field_test.go
@@ -22,6 +22,11 @@ import (
 	"github.com/jhump/protoreflect/desc/builder"
 )
 
+type field struct {
+	fieldName string
+	fieldType *builder.FieldType
+}
+
 func TestParentField(t *testing.T) {
 	// Set up the testing permutations.
 	tests := []struct {
diff --git a/rules/rules_test.go b/rules/rules_test.go
new file mode 100644
index 0000000..dab3314
--- /dev/null
+++ b/rules/rules_test.go
@@ -0,0 +1,51 @@
+package rules
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/googleapis/api-linter/lint"
+)
+
+func TestAddAIPRules(t *testing.T) {
+	wantError := errors.New("test")
+	tests := []struct {
+		name          string
+		addRulesFuncs []addRulesFuncType
+		err           error
+	}{
+		{
+			name:          "EmptyRules_NoError",
+			addRulesFuncs: nil,
+			err:           nil,
+		},
+		{
+			name: "AddingRules_NoError",
+			addRulesFuncs: []addRulesFuncType{
+				func(lint.RuleRegistry) error { return nil },
+			},
+			err: nil,
+		},
+		{
+			name: "ReturnError",
+			addRulesFuncs: []addRulesFuncType{
+				func(lint.RuleRegistry) error { return wantError },
+			},
+			err: wantError,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			err := addAIPRules(lint.NewRuleRegistry(), test.addRulesFuncs)
+			if err != test.err {
+				t.Errorf("addAIPRules got error %v, but want %v", err, test.err)
+			}
+		})
+	}
+}
+
+func TestAdd(t *testing.T) {
+	if err := Add(lint.NewRuleRegistry()); err != nil {
+		t.Errorf("Add got an error: %v", err)
+	}
+}