func IsPluginInstalled(pluginName, pluginVersion string) bool {
	pluginsInstallDir, err := common.GetPluginsInstallDir(pluginName)
	if err != nil {
		return false
	}
	thisPluginDir := filepath.Join(pluginsInstallDir, pluginName)
	if !common.DirExists(thisPluginDir) {
		return false
	}
	if pluginVersion != "" {
		return common.FileExists(filepath.Join(thisPluginDir, pluginVersion, common.PluginJSONFile))
	}
	return true
}
IsPluginInstalled checks if given plugin with specific version is installed or not.
package plugin
import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/getgauge/common"
	"github.com/getgauge/gauge-proto/go/gauge_messages"
	"github.com/getgauge/gauge/api/infoGatherer"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/conn"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/manifest"
	"github.com/getgauge/gauge/plugin/pluginInfo"
	"github.com/getgauge/gauge/version"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"
)
type pluginScope string
const (
	executionScope          pluginScope = "execution"
	docScope                pluginScope = "documentation"
	pluginConnectionPortEnv             = "plugin_connection_port"
)
type plugin struct {
	mutex            *sync.Mutex
	connection       net.Conn
	gRPCConn         *grpc.ClientConn
	ReporterClient   gauge_messages.ReporterClient
	DocumenterClient gauge_messages.DocumenterClient
	pluginCmd        *exec.Cmd
	descriptor       *PluginDescriptor
	killTimer        *time.Timer
}
func isProcessRunning(p *plugin) bool {
	p.mutex.Lock()
	ps := p.pluginCmd.ProcessState
	p.mutex.Unlock()
	return ps == nil || !ps.Exited()
}
func (p *plugin) killGrpcProcess() error {
	var m *gauge_messages.Empty
	var err error
	if p.ReporterClient != nil {
		m, err = p.ReporterClient.Kill(context.Background(), &gauge_messages.KillProcessRequest{})
	} else if p.DocumenterClient != nil {
		m, err = p.DocumenterClient.Kill(context.Background(), &gauge_messages.KillProcessRequest{})
	}
	if m == nil || err != nil {
		errStatus, _ := status.FromError(err)
		if errStatus.Code() == codes.Unavailable {
			return nil
		}
		return err
	}
	if p.gRPCConn == nil && p.pluginCmd == nil {
		return nil
	}
	defer p.gRPCConn.Close()
	if isProcessRunning(p) {
		exited := make(chan bool, 1)
		go func() {
			for {
				if isProcessRunning(p) {
					time.Sleep(100 * time.Millisecond)
				} else {
					exited <- true
					return
				}
			}
		}()
		select {
		case done := <-exited:
			if done {
				logger.Debugf(true, "Runner with PID:%d has exited", p.pluginCmd.Process.Pid)
				return nil
			}
		case <-time.After(config.PluginKillTimeout()):
			logger.Warningf(true, "Killing runner with PID:%d forcefully", p.pluginCmd.Process.Pid)
			return p.pluginCmd.Process.Kill()
		}
	}
	return nil
}
func (p *plugin) kill(wg *sync.WaitGroup) error {
	defer wg.Done()
	if p.gRPCConn != nil && p.ReporterClient != nil {
		return p.killGrpcProcess()
	}
	if isProcessRunning(p) {
		defer p.connection.Close()
		p.killTimer = time.NewTimer(config.PluginKillTimeout())
		err := conn.SendProcessKillMessage(p.connection)
		if err != nil {
			logger.Warningf(true, "Error while killing plugin %s : %s ", p.descriptor.Name, err.Error())
		}
		exited := make(chan bool, 1)
		go func() {
			for {
				if isProcessRunning(p) {
					time.Sleep(100 * time.Millisecond)
				} else {
					exited <- true
					return
				}
			}
		}()
		select {
		case <-exited:
			if !p.killTimer.Stop() {
				<-p.killTimer.C
			}
			logger.Debugf(true, "Plugin [%s] with pid [%d] has exited", p.descriptor.Name, p.pluginCmd.Process.Pid)
		case <-p.killTimer.C:
			logger.Warningf(true, "Plugin [%s] with pid [%d] did not exit after %.2f seconds. Forcefully killing it.", p.descriptor.Name, p.pluginCmd.Process.Pid, config.PluginKillTimeout().Seconds())
			err := p.pluginCmd.Process.Kill()
			if err != nil {
				logger.Warningf(true, "Error while killing plugin %s : %s ", p.descriptor.Name, err.Error())
			}
			return err
		}
	}
	return nil
}