feat: cache template environment data

This commit is contained in:
Jan De Dobbeleer 2022-01-18 09:48:47 +01:00 committed by Jan De Dobbeleer
parent 9e77d0f939
commit e5dd07fb9a
17 changed files with 142 additions and 141 deletions

View file

@ -1,7 +1,6 @@
package main
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -57,11 +56,17 @@ func TestGetConsoleTitle(t *testing.T) {
env.On("getcwd").Return(tc.Cwd)
env.On("homeDir").Return("/usr/home")
env.On("getPathSeperator").Return(tc.PathSeperator)
env.On("isRunningAsRoot").Return(tc.Root)
env.On("getShellName").Return(tc.ShellName)
env.On("getenv", "USERDOMAIN").Return("MyCompany")
env.On("getCurrentUser").Return("MyUser")
env.On("getHostName").Return("MyHost", nil)
env.On("templateCache").Return(&templateCache{
Env: map[string]string{
"USERDOMAIN": "MyCompany",
},
Shell: tc.ShellName,
UserName: "MyUser",
Root: tc.Root,
HostName: "MyHost",
PWD: tc.Cwd,
Folder: base(tc.Cwd, env),
})
env.onTemplate()
ansi := &ansiUtils{}
ansi.init(tc.ShellName)
@ -112,12 +117,15 @@ func TestGetConsoleTitleIfGethostnameReturnsError(t *testing.T) {
env := new(MockedEnvironment)
env.On("getcwd").Return(tc.Cwd)
env.On("homeDir").Return("/usr/home")
env.On("getPathSeperator").Return(tc.PathSeperator)
env.On("isRunningAsRoot").Return(tc.Root)
env.On("getShellName").Return(tc.ShellName)
env.On("getenv", "USERDOMAIN").Return("MyCompany")
env.On("getCurrentUser").Return("MyUser")
env.On("getHostName").Return("", fmt.Errorf("I have a bad feeling about this"))
env.On("templateCache").Return(&templateCache{
Env: map[string]string{
"USERDOMAIN": "MyCompany",
},
Shell: tc.ShellName,
UserName: "MyUser",
Root: tc.Root,
HostName: "",
})
env.onTemplate()
ansi := &ansiUtils{}
ansi.init(tc.ShellName)

View file

@ -90,7 +90,6 @@ type wifiInfo struct {
type Environment interface {
getenv(key string) string
environ() map[string]string
getcwd() string
homeDir() string
hasFiles(pattern string) bool
@ -128,6 +127,7 @@ type Environment interface {
convertToLinuxPath(path string) string
convertToWindowsPath(path string) string
getWifiNetwork() (*wifiInfo, error)
templateCache() *templateCache
}
type commandCache struct {
@ -155,13 +155,13 @@ const (
)
type environment struct {
args *args
cwd string
cmdCache *commandCache
fileCache *fileCache
environCache map[string]string
logBuilder strings.Builder
debug bool
args *args
cwd string
cmdCache *commandCache
fileCache *fileCache
tmplCache *templateCache
logBuilder strings.Builder
debug bool
}
func (env *environment) init(args *args) {
@ -176,18 +176,6 @@ func (env *environment) init(args *args) {
}
env.fileCache = &fileCache{}
env.fileCache.init(env.getCachePath())
env.environCache = make(map[string]string)
const separator = "="
values := os.Environ()
for value := range values {
splitted := strings.Split(values[value], separator)
if len(splitted) != 2 {
continue
}
key := splitted[0]
val := splitted[1:]
env.environCache[key] = strings.Join(val, separator)
}
}
func (env *environment) resolveConfigPath() {
@ -236,11 +224,6 @@ func (env *environment) getenv(key string) string {
return val
}
func (env *environment) environ() map[string]string {
defer env.trace(time.Now(), "environ")
return env.environCache
}
func (env *environment) getcwd() string {
defer env.trace(time.Now(), "getcwd")
if env.cwd != "" {
@ -563,6 +546,42 @@ func (env *environment) logs() string {
return env.logBuilder.String()
}
func (env *environment) templateCache() *templateCache {
defer env.trace(time.Now(), "templateCache")
if env.tmplCache != nil {
return env.tmplCache
}
tmplCache := &templateCache{
Root: env.isRunningAsRoot(),
Shell: env.getShellName(),
Code: env.lastErrorCode(),
}
tmplCache.Env = make(map[string]string)
const separator = "="
values := os.Environ()
for value := range values {
splitted := strings.Split(values[value], separator)
if len(splitted) != 2 {
continue
}
key := splitted[0]
val := splitted[1:]
tmplCache.Env[key] = strings.Join(val, separator)
}
pwd := env.getcwd()
pwd = strings.Replace(pwd, env.homeDir(), "~", 1)
tmplCache.PWD = pwd
tmplCache.Folder = base(pwd, env)
tmplCache.UserName = env.getCurrentUser()
if host, err := env.getHostName(); err == nil {
tmplCache.HostName = host
}
goos := env.getRuntimeGOOS()
tmplCache.OS = goos
env.tmplCache = tmplCache
return env.tmplCache
}
func cleanHostName(hostName string) string {
garbage := []string{
".lan",

View file

@ -53,6 +53,9 @@ func (env *environment) getTerminalWidth() (int, error) {
}
func (env *environment) getPlatform() string {
if wsl := env.getenv("WSL_DISTRO_NAME"); len(wsl) != 0 {
return strings.ToLower(wsl)
}
p, _, _, _ := host.PlatformInformation()
return p
}

View file

@ -336,18 +336,9 @@ func getConsoleBackgroundColor(env Environment, backgroundColorTemplate string)
if len(backgroundColorTemplate) == 0 {
return backgroundColorTemplate
}
context := struct {
Env map[string]string
}{
Env: map[string]string{},
}
matches := findAllNamedRegexMatch(templateEnvRegex, backgroundColorTemplate)
for _, match := range matches {
context.Env[match["ENV"]] = env.getenv(match["ENV"])
}
template := &textTemplate{
Template: backgroundColorTemplate,
Context: context,
Context: nil,
Env: env,
}
text, err := template.render()

View file

@ -18,7 +18,11 @@ func TestConsoleBackgroundColorTemplate(t *testing.T) {
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getenv", "TERM_PROGRAM").Return(tc.Term)
env.On("templateCache").Return(&templateCache{
Env: map[string]string{
"TERM_PROGRAM": tc.Term,
},
})
env.onTemplate()
color := getConsoleBackgroundColor(env, "{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
assert.Equal(t, tc.Expected, color, tc.Case)

View file

@ -70,6 +70,8 @@ func TestGetBatteryColors(t *testing.T) {
},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.onTemplate()
batt := &batt{
Percentage: tc.Percentage,
}
@ -78,6 +80,7 @@ func TestGetBatteryColors(t *testing.T) {
}
segment := &Segment{
writer: batt,
env: env,
}
segment.Foreground = tc.DefaultColor
segment.ForegroundTemplates = tc.Templates

View file

@ -77,6 +77,9 @@ func TestExitWriterTemplateString(t *testing.T) {
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("lastErrorCode").Return(tc.ExitCode)
env.On("templateCache").Return(&templateCache{
Code: tc.ExitCode,
})
env.onTemplate()
props := properties{
SegmentTemplate: tc.Template,

View file

@ -738,6 +738,9 @@ func TestGitTemplateString(t *testing.T) {
props := properties{
FetchStatus: true,
}
env := new(MockedEnvironment)
env.onTemplate()
tc.Git.env = env
tc.Git.props = props
assert.Equal(t, tc.Expected, tc.Git.templateString(tc.Template), tc.Case)
}

View file

@ -2,7 +2,6 @@ package main
import (
"fmt"
"strings"
)
type osInfo struct {
@ -79,17 +78,14 @@ func (n *osInfo) string() string {
n.os = darwinPlatform
return n.props.getString(MacOS, "\uF179")
case linuxPlatform:
wsl := n.env.getenv("WSL_DISTRO_NAME")
p := n.env.getPlatform()
if len(wsl) == 0 {
n.os = p
return n.getDistroName(p, "")
n.os = n.env.getPlatform()
if !n.env.isWsl() {
return n.getDistroName(n.os, "")
}
n.os = strings.ToLower(wsl)
return fmt.Sprintf("%s%s%s",
n.props.getString(WSL, "WSL"),
n.props.getString(WSLSeparator, " - "),
n.getDistroName(p, wsl))
n.getDistroName(n.os, n.os))
default:
n.os = goos
return goos

View file

@ -11,7 +11,7 @@ func TestOSInfo(t *testing.T) {
Case string
ExpectedString string
GOOS string
WSLDistro string
IsWSL bool
Platform string
DisplayDistroName bool
}{
@ -19,14 +19,14 @@ func TestOSInfo(t *testing.T) {
Case: "WSL debian - icon",
ExpectedString: "WSL at \uf306",
GOOS: "linux",
WSLDistro: "debian",
IsWSL: true,
Platform: "debian",
},
{
Case: "WSL debian - name",
ExpectedString: "WSL at burps",
ExpectedString: "WSL at debian",
GOOS: "linux",
WSLDistro: "burps",
IsWSL: true,
Platform: "debian",
DisplayDistroName: true,
},
@ -62,7 +62,7 @@ func TestOSInfo(t *testing.T) {
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS").Return(tc.GOOS)
env.On("getenv", "WSL_DISTRO_NAME").Return(tc.WSLDistro)
env.On("isWsl").Return(tc.IsWSL)
env.On("getPlatform").Return(tc.Platform)
osInfo := &osInfo{
env: env,
@ -75,9 +75,7 @@ func TestOSInfo(t *testing.T) {
},
}
assert.Equal(t, tc.ExpectedString, osInfo.string(), tc.Case)
if tc.WSLDistro != "" {
assert.Equal(t, tc.WSLDistro, osInfo.os, tc.Case)
} else if tc.Platform != "" {
if tc.Platform != "" {
assert.Equal(t, tc.Platform, osInfo.os, tc.Case)
} else {
assert.Equal(t, tc.GOOS, osInfo.os, tc.Case)

View file

@ -19,11 +19,6 @@ func (env *MockedEnvironment) getenv(key string) string {
return args.String(0)
}
func (env *MockedEnvironment) environ() map[string]string {
args := env.Called()
return args.Get(0).(map[string]string)
}
func (env *MockedEnvironment) getcwd() string {
args := env.Called()
return args.String(0)
@ -208,38 +203,20 @@ func (env *MockedEnvironment) getWifiNetwork() (*wifiInfo, error) {
return args.Get(0).(*wifiInfo), args.Error(1)
}
func (env *MockedEnvironment) templateCache() *templateCache {
args := env.Called()
return args.Get(0).(*templateCache)
}
func (env *MockedEnvironment) onTemplate() {
patchMethodIfNotSpecified := func(method string, returnArguments ...interface{}) {
for _, call := range env.Mock.ExpectedCalls {
if call.Method == method {
return
}
for _, call := range env.Mock.ExpectedCalls {
if call.Method == "templateCache" {
return
}
env.On(method).Return(returnArguments...)
}
patchEnvVars := func() map[string]string {
keyValueArray := make(map[string]string)
for _, call := range env.Mock.ExpectedCalls {
if call.Method == "getenv" {
keyValueArray[call.Arguments.String(0)] = call.ReturnArguments.String(0)
}
}
return keyValueArray
}
patchMethodIfNotSpecified("isRunningAsRoot", false)
patchMethodIfNotSpecified("getcwd", "/usr/home/dev/my-app")
patchMethodIfNotSpecified("homeDir", "/usr/home/dev")
patchMethodIfNotSpecified("getPathSeperator", "/")
patchMethodIfNotSpecified("getShellName", "pwsh")
patchMethodIfNotSpecified("getCurrentUser", "dev")
patchMethodIfNotSpecified("getHostName", "laptop", nil)
patchMethodIfNotSpecified("lastErrorCode", 0)
patchMethodIfNotSpecified("getRuntimeGOOS", darwinPlatform)
if env.getRuntimeGOOS() == linuxPlatform {
env.On("getenv", "WSL_DISTRO_NAME").Return("ubuntu")
env.On("getPlatform").Return("ubuntu")
}
patchMethodIfNotSpecified("environ", patchEnvVars())
env.On("templateCache").Return(&templateCache{
Env: make(map[string]string),
})
}
const (

View file

@ -328,6 +328,9 @@ func TestPlasticTemplateString(t *testing.T) {
FetchStatus: true,
}
tc.Plastic.props = props
env := new(MockedEnvironment)
env.onTemplate()
tc.Plastic.env = env
assert.Equal(t, tc.Expected, tc.Plastic.templateString(tc.Template), tc.Case)
}
}

View file

@ -105,9 +105,17 @@ func TestSessionSegmentTemplate(t *testing.T) {
}
env.On("getenv", "SSH_CONNECTION").Return(SSHSession)
env.On("getenv", "SSH_CLIENT").Return(SSHSession)
env.On("isRunningAsRoot").Return(tc.Root)
env.On("getenv", defaultUserEnvVar).Return(tc.DefaultUserName)
env.onTemplate()
env.On("templateCache").Return(&templateCache{
UserName: tc.UserName,
HostName: tc.ComputerName,
Env: map[string]string{
"SSH_CONNECTION": SSHSession,
"SSH_CLIENT": SSHSession,
defaultUserEnvVar: tc.DefaultUserName,
},
Root: tc.Root,
})
session := &session{
env: env,
props: properties{

View file

@ -190,11 +190,14 @@ func TestGetColors(t *testing.T) {
},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.onTemplate()
segment := &Segment{
writer: &aws{
Profile: tc.Profile,
Region: tc.Region,
},
env: env,
}
if tc.Background {
segment.Background = tc.DefaultColor

View file

@ -24,16 +24,18 @@ func TestTextSegment(t *testing.T) {
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getcwd").Return("/usr/home/posh")
env.On("homeDir").Return("/usr/home")
env.On("getPathSeperator").Return("/")
env.On("isRunningAsRoot").Return(true)
env.On("getShellName").Return("terminal")
env.On("getenv", "HELLO").Return("hello")
env.On("getenv", "WORLD").Return("")
env.On("getCurrentUser").Return("Posh")
env.On("getHostName").Return("MyHost", nil)
env.onTemplate()
env.On("templateCache").Return(&templateCache{
UserName: "Posh",
Env: map[string]string{
"HELLO": "hello",
"WORLD": "",
},
HostName: "MyHost",
Shell: "terminal",
Root: true,
Folder: base("/usr/home/posh", env),
})
txt := &text{
env: env,
props: properties{

View file

@ -12,9 +12,6 @@ const (
// Errors to show when the template handling fails
invalidTemplate = "invalid template text"
incorrectTemplate = "unable to create text based on template"
// nostruct = "unable to create map from non-struct type"
templateEnvRegex = `\.Env\.(?P<ENV>[^ \.}]*)`
)
type textTemplate struct {
@ -26,6 +23,13 @@ type textTemplate struct {
type Data interface{}
type Context struct {
templateCache
// Simple container to hold ANY object
Data
}
type templateCache struct {
Root bool
PWD string
Folder string
@ -35,37 +39,14 @@ type Context struct {
Code int
Env map[string]string
OS string
// Simple container to hold ANY object
Data
}
func (c *Context) init(t *textTemplate) {
c.Data = t.Context
if t.Env == nil {
if cache := t.Env.templateCache(); cache != nil {
c.templateCache = *cache
return
}
c.Root = t.Env.isRunningAsRoot()
pwd := t.Env.getcwd()
pwd = strings.Replace(pwd, t.Env.homeDir(), "~", 1)
c.PWD = pwd
c.Folder = base(c.PWD, t.Env)
c.Shell = t.Env.getShellName()
c.UserName = t.Env.getCurrentUser()
if host, err := t.Env.getHostName(); err == nil {
c.HostName = host
}
c.Code = t.Env.lastErrorCode()
c.Env = t.Env.environ()
goos := t.Env.getRuntimeGOOS()
if goos == linuxPlatform {
wsl := t.Env.getenv("WSL_DISTRO_NAME")
goos = t.Env.getPlatform()
if len(wsl) != 0 {
goos = strings.ToLower(wsl)
}
}
c.OS = goos
}
func (t *textTemplate) render() (string, error) {

View file

@ -121,10 +121,9 @@ func TestRenderTemplateEnvVar(t *testing.T) {
}
for _, tc := range cases {
env := &MockedEnvironment{}
for name, value := range tc.Env {
env.On("getenv", name).Return(value)
}
env.onTemplate()
env.On("templateCache").Return(&templateCache{
Env: tc.Env,
})
template := &textTemplate{
Template: tc.Template,
Context: tc.Context,