Skip to content
Permalink
Browse files

plugin: add unit test for plugin (#10506)

  • Loading branch information...
lysu authored and jackysp committed Jun 10, 2019
1 parent 59b5e1d commit 2ee59f174da3830c5e30464012c08fd3169f42d1
Showing with 418 additions and 44 deletions.
  1. +42 −0 plugin/const_test.go
  2. +54 −0 plugin/helper_test.go
  3. +41 −44 plugin/plugin.go
  4. +281 −0 plugin/plugin_test.go
@@ -0,0 +1,42 @@
// Copyright 2019 PingCAP, Inc.
//
// 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
//
// http://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,
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import (
"fmt"
"testing"
)

func TestConstToString(t *testing.T) {
kinds := map[fmt.Stringer]string{
Audit: "Audit",
Authentication: "Authentication",
Schema: "Schema",
Daemon: "Daemon",
Uninitialized: "Uninitialized",
Ready: "Ready",
Dying: "Dying",
Disable: "Disable",
Connected: "Connected",
Disconnect: "Disconnect",
ChangeUser: "ChangeUser",
PreAuth: "PreAuth",
ConnectionEvent(byte(15)): "",
}
for key, value := range kinds {
if key.String() != value {
t.Errorf("kind %s != %s", key.String(), kinds)
}
}
}
@@ -0,0 +1,54 @@
// Copyright 2019 PingCAP, Inc.
//
// 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
//
// http://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,
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import "testing"

func TestPluginDeclare(t *testing.T) {
auditRaw := &AuditManifest{Manifest: Manifest{}}
auditExport := ExportManifest(auditRaw)
audit2 := DeclareAuditManifest(auditExport)
if audit2 != auditRaw {
t.Errorf("declare audit fail")
}

authRaw := &AuthenticationManifest{Manifest: Manifest{}}
authExport := ExportManifest(authRaw)
auth2 := DeclareAuthenticationManifest(authExport)
if auth2 != authRaw {
t.Errorf("declare auth fail")
}

schemaRaw := &SchemaManifest{Manifest: Manifest{}}
schemaExport := ExportManifest(schemaRaw)
schema2 := DeclareSchemaManifest(schemaExport)
if schema2 != schemaRaw {
t.Errorf("declare schema fail")
}

daemonRaw := &DaemonManifest{Manifest: Manifest{}}
daemonExport := ExportManifest(daemonRaw)
daemon2 := DeclareDaemonManifest(daemonExport)
if daemon2 != daemonRaw {
t.Errorf("declare daemon fail")
}
}

func TestDecode(t *testing.T) {
failID := ID("fail")
_, _, err := failID.Decode()
if err == nil {
t.Errorf("'fail' should not decode success")
}
}
@@ -49,8 +49,9 @@ type plugins struct {
// clone deep copies plugins info.
func (p *plugins) clone() *plugins {
np := &plugins{
plugins: make(map[Kind][]Plugin, len(p.plugins)),
versions: make(map[string]uint16, len(p.versions)),
plugins: make(map[Kind][]Plugin, len(p.plugins)),
versions: make(map[string]uint16, len(p.versions)),
dyingPlugins: make([]Plugin, len(p.dyingPlugins)),
}
for key, value := range p.plugins {
np.plugins[key] = append([]Plugin(nil), value...)
@@ -99,34 +100,7 @@ type Plugin struct {
Path string
}

type validateMode int

const (
initMode validateMode = iota
reloadMode
)

func (p *Plugin) validate(ctx context.Context, tiPlugins *plugins, mode validateMode) error {
if mode == reloadMode {
var oldPlugin *Plugin
for i, item := range tiPlugins.plugins[p.Kind] {
if item.Name == p.Name {
oldPlugin = &tiPlugins.plugins[p.Kind][i]
break
}
}
if oldPlugin == nil {
return errUnsupportedReloadPlugin.GenWithStackByArgs(p.Name)
}
if len(p.SysVars) != len(oldPlugin.SysVars) {
return errUnsupportedReloadPluginVar.GenWithStackByArgs("")
}
for varName, varVal := range p.SysVars {
if oldPlugin.SysVars[varName] == nil || *oldPlugin.SysVars[varName] != *varVal {
return errUnsupportedReloadPluginVar.GenWithStackByArgs(varVal)
}
}
}
func (p *Plugin) validate(ctx context.Context, tiPlugins *plugins) error {
if p.RequireVersion != nil {
for component, reqVer := range p.RequireVersion {
if ver, ok := tiPlugins.versions[component]; !ok || ver < reqVer {
@@ -197,7 +171,7 @@ func Load(ctx context.Context, cfg Config) (err error) {
// Cross validate & Load plugins.
for kind := range tiPlugins.plugins {
for i := range tiPlugins.plugins[kind] {
if err = tiPlugins.plugins[kind][i].validate(ctx, tiPlugins, initMode); err != nil {
if err = tiPlugins.plugins[kind][i].validate(ctx, tiPlugins); err != nil {
if cfg.SkipWhenFail {
logutil.Logger(ctx).Warn("validate plugin fail and disable plugin",
zap.String("plugin", tiPlugins.plugins[kind][i].Name), zap.Error(err))
@@ -284,26 +258,25 @@ func (w *flushWatcher) watchLoop() {
}
}

type loadFn func(plugin *Plugin, dir string, pluginID ID) (manifest func() *Manifest, err error)

var testHook *struct {
loadOne loadFn
}

func loadOne(dir string, pluginID ID) (plugin Plugin, err error) {
plugin.Path = filepath.Join(dir, string(pluginID)+LibrarySuffix)
plugin.library, err = gplugin.Open(plugin.Path)
if err != nil {
err = errors.Trace(err)
return
}
manifestSym, err := plugin.library.Lookup(ManifestSymbol)
pName, pVersion, err := pluginID.Decode()
if err != nil {
err = errors.Trace(err)
return
}
manifest, ok := manifestSym.(func() *Manifest)
if !ok {
err = errInvalidPluginManifest.GenWithStackByArgs(string(pluginID))
return
var manifest func() *Manifest
if testHook == nil {
manifest, err = loadManifestByGoPlugin(&plugin, dir, pluginID)
} else {
manifest, err = testHook.loadOne(&plugin, dir, pluginID)
}
pName, pVersion, err := pluginID.Decode()
if err != nil {
err = errors.Trace(err)
return
}
plugin.Manifest = manifest()
@@ -318,6 +291,27 @@ func loadOne(dir string, pluginID ID) (plugin Plugin, err error) {
return
}

func loadManifestByGoPlugin(plugin *Plugin, dir string, pluginID ID) (manifest func() *Manifest, err error) {
plugin.Path = filepath.Join(dir, string(pluginID)+LibrarySuffix)
plugin.library, err = gplugin.Open(plugin.Path)
if err != nil {
err = errors.Trace(err)
return
}
manifestSym, err := plugin.library.Lookup(ManifestSymbol)
if err != nil {
err = errors.Trace(err)
return
}
var ok bool
manifest, ok = manifestSym.(func() *Manifest)
if !ok {
err = errInvalidPluginManifest.GenWithStackByArgs(string(pluginID))
return
}
return
}

// Shutdown cleanups all plugin resources.
// Notice: it just cleanups the resource of plugin, but cannot unload plugins(limited by go plugin).
func Shutdown(ctx context.Context) {
@@ -332,6 +326,9 @@ func Shutdown(ctx context.Context) {
if p.flushWatcher != nil {
p.flushWatcher.cancel()
}
if p.OnShutdown == nil {
continue
}
if err := p.OnShutdown(ctx, p.Manifest); err != nil {
logutil.Logger(ctx).Error("call OnShutdown for failure",
zap.String("plugin", p.Name), zap.Error(err))

0 comments on commit 2ee59f1

Please sign in to comment.
You can’t perform that action at this time.