]> git.lizzy.rs Git - micro.git/commitdiff
Add simulation screen tests
authorZachary Yedidia <zyedidia@gmail.com>
Sat, 20 Jun 2020 22:24:12 +0000 (18:24 -0400)
committerZachary Yedidia <zyedidia@gmail.com>
Sat, 20 Jun 2020 22:24:12 +0000 (18:24 -0400)
Makefile
cmd/micro/micro.go
cmd/micro/micro_test.go [new file with mode: 0644]
go.mod
go.sum
internal/action/bindings.go
internal/action/defaults_darwin.go
internal/action/defaults_other.go
internal/screen/screen.go

index de83391d39c2263c91cd35f1270e0ed4a2270c5d..2f03e9aec04e22d0904afd68839ec232fa592091 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -64,6 +64,7 @@ testgen:
 
 test:
        go test ./internal/...
+       go test ./cmd/...
 
 bench:
        for i in 1 2 3; do \
index 275ff5032a111fd4c61d25589697a1475ced7287..7c43c4a7060abf84e15abc660e653a6b79261947 100644 (file)
@@ -132,7 +132,7 @@ func DoPluginFlags() {
 
 // LoadInput determines which files should be loaded into buffers
 // based on the input stored in flag.Args()
-func LoadInput() []*buffer.Buffer {
+func LoadInput(args []string) []*buffer.Buffer {
        // There are a number of ways micro should start given its input
 
        // 1. If it is given a files in flag.Args(), it should open those
@@ -147,7 +147,6 @@ func LoadInput() []*buffer.Buffer {
        var filename string
        var input []byte
        var err error
-       args := flag.Args()
        buffers := make([]*buffer.Buffer, 0, len(args))
 
        btype := buffer.BTDefault
@@ -262,7 +261,12 @@ func main() {
 
        DoPluginFlags()
 
-       screen.Init()
+       err = screen.Init()
+       if err != nil {
+               fmt.Println(err)
+               fmt.Println("Fatal: Micro could not initialize a Screen.")
+               os.Exit(1)
+       }
 
        defer func() {
                if err := recover(); err != nil {
@@ -291,7 +295,8 @@ func main() {
                screen.TermMessage(err)
        }
 
-       b := LoadInput()
+       args := flag.Args()
+       b := LoadInput(args)
 
        if len(b) == 0 {
                // No buffers to open
diff --git a/cmd/micro/micro_test.go b/cmd/micro/micro_test.go
new file mode 100644 (file)
index 0000000..7029a93
--- /dev/null
@@ -0,0 +1,341 @@
+package main
+
+import (
+       "fmt"
+       "io/ioutil"
+       "log"
+       "os"
+       "testing"
+
+       "github.com/go-errors/errors"
+       "github.com/stretchr/testify/assert"
+       "github.com/zyedidia/micro/v2/internal/action"
+       "github.com/zyedidia/micro/v2/internal/buffer"
+       "github.com/zyedidia/micro/v2/internal/config"
+       "github.com/zyedidia/micro/v2/internal/screen"
+       "github.com/zyedidia/tcell"
+)
+
+var tempDir string
+var sim tcell.SimulationScreen
+
+func init() {
+       events = make(chan tcell.Event, 8)
+}
+
+func startup(args []string) (tcell.SimulationScreen, error) {
+       var err error
+
+       tempDir, err = ioutil.TempDir("", "micro_test")
+       if err != nil {
+               return nil, err
+       }
+       err = config.InitConfigDir(tempDir)
+       if err != nil {
+               return nil, err
+       }
+
+       config.InitRuntimeFiles()
+       err = config.ReadSettings()
+       if err != nil {
+               return nil, err
+       }
+       err = config.InitGlobalSettings()
+       if err != nil {
+               return nil, err
+       }
+
+       s, err := screen.InitSimScreen()
+       if err != nil {
+               return nil, err
+       }
+
+       defer func() {
+               if err := recover(); err != nil {
+                       screen.Screen.Fini()
+                       fmt.Println("Micro encountered an error:", err)
+                       // backup all open buffers
+                       for _, b := range buffer.OpenBuffers {
+                               b.Backup(false)
+                       }
+                       // Print the stack trace too
+                       log.Fatalf(errors.Wrap(err, 2).ErrorStack())
+                       os.Exit(1)
+               }
+       }()
+
+       err = config.LoadAllPlugins()
+       if err != nil {
+               screen.TermMessage(err)
+       }
+
+       action.InitBindings()
+       action.InitCommands()
+
+       err = config.InitColorscheme()
+       if err != nil {
+               return nil, err
+       }
+
+       b := LoadInput(args)
+
+       if len(b) == 0 {
+               return nil, errors.New("No buffers opened")
+       }
+
+       action.InitTabs(b)
+       action.InitGlobals()
+
+       err = config.RunPluginFn("init")
+       if err != nil {
+               return nil, err
+       }
+
+       s.InjectResize()
+       handleEvent()
+
+       return s, nil
+}
+
+func cleanup() {
+       os.RemoveAll(tempDir)
+}
+
+func handleEvent() {
+       screen.Lock()
+       e := screen.Screen.PollEvent()
+       screen.Unlock()
+       if e != nil {
+               events <- e
+       }
+       DoEvent()
+}
+
+func injectKey(key tcell.Key, r rune, mod tcell.ModMask) {
+       sim.InjectKey(key, r, mod)
+       handleEvent()
+}
+
+func injectMouse(x, y int, buttons tcell.ButtonMask, mod tcell.ModMask) {
+       sim.InjectMouse(x, y, buttons, mod)
+       handleEvent()
+}
+
+func injectString(str string) {
+       // the tcell simulation screen event channel can only handle
+       // 10 events at once, so we need to divide up the key events
+       // into chunks of 10 and handle the 10 events before sending
+       // another chunk of events
+       iters := len(str) / 10
+       extra := len(str) % 10
+
+       for i := 0; i < iters; i++ {
+               s := i * 10
+               e := i*10 + 10
+               sim.InjectKeyBytes([]byte(str[s:e]))
+               for i := 0; i < 10; i++ {
+                       handleEvent()
+               }
+       }
+
+       sim.InjectKeyBytes([]byte(str[len(str)-extra:]))
+       for i := 0; i < extra; i++ {
+               handleEvent()
+       }
+}
+
+func openFile(file string) {
+       injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl)
+       injectString(fmt.Sprintf("open %s", file))
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+}
+
+func createTestFile(name string, content string) (string, error) {
+       testf, err := ioutil.TempFile("", name)
+       if err != nil {
+               return "", err
+       }
+
+       if _, err := testf.Write([]byte(content)); err != nil {
+               return "", err
+       }
+       if err := testf.Close(); err != nil {
+               return "", err
+       }
+
+       return testf.Name(), nil
+}
+
+func TestMain(m *testing.M) {
+       var err error
+       sim, err = startup([]string{})
+       if err != nil {
+               log.Fatalln(err)
+               os.Exit(1)
+       }
+
+       retval := m.Run()
+       cleanup()
+
+       os.Exit(retval)
+}
+
+func TestSimpleEdit(t *testing.T) {
+       file, err := createTestFile("micro_simple_edit_test", "base content")
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       defer os.Remove(file)
+
+       openFile(file)
+
+       var buf *buffer.Buffer
+       for _, b := range buffer.OpenBuffers {
+               if b.Path == file {
+                       buf = b
+               }
+       }
+
+       if buf == nil {
+               t.Errorf("Could not find buffer %s", file)
+               return
+       }
+
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+       injectKey(tcell.KeyUp, 0, tcell.ModNone)
+       injectString("first line")
+
+       // test both kinds of backspace
+       for i := 0; i < len("ne"); i++ {
+               injectKey(tcell.KeyBackspace, rune(tcell.KeyBackspace), tcell.ModNone)
+       }
+       for i := 0; i < len(" li"); i++ {
+               injectKey(tcell.KeyBackspace2, rune(tcell.KeyBackspace2), tcell.ModNone)
+       }
+       injectString("foobar")
+
+       injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl)
+
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       assert.Equal(t, "firstfoobar\nbase content\n", string(data))
+}
+
+func TestMouse(t *testing.T) {
+       file, err := createTestFile("micro_mouse_test", "base content")
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       defer os.Remove(file)
+
+       openFile(file)
+
+       // buffer:
+       // base content
+       // the selections need to happen at different locations to avoid a double click
+       injectMouse(3, 0, tcell.Button1, tcell.ModNone)
+       injectKey(tcell.KeyLeft, 0, tcell.ModNone)
+       injectMouse(0, 0, tcell.ButtonNone, tcell.ModNone)
+       injectString("secondline")
+       // buffer:
+       // secondlinebase content
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+       // buffer:
+       // secondline
+       // base content
+       injectMouse(2, 0, tcell.Button1, tcell.ModNone)
+       injectMouse(0, 0, tcell.ButtonNone, tcell.ModNone)
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+       // buffer:
+       //
+       // secondline
+       // base content
+       injectKey(tcell.KeyUp, 0, tcell.ModNone)
+       injectString("firstline")
+       // buffer:
+       // firstline
+       // secondline
+       // base content
+       injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl)
+
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       assert.Equal(t, "firstline\nsecondline\nbase content\n", string(data))
+}
+
+var srTestStart = `foo
+foo
+foofoofoo
+Ernleȝe foo æðelen
+`
+var srTest2 = `test_string
+test_string
+test_stringtest_stringtest_string
+Ernleȝe test_string æðelen
+`
+var srTest3 = `test_foo
+test_string
+test_footest_stringtest_foo
+Ernleȝe test_string æðelen
+`
+
+func TestSearchAndReplace(t *testing.T) {
+       file, err := createTestFile("micro_search_replace_test", srTestStart)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       defer os.Remove(file)
+
+       openFile(file)
+
+       injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl)
+       injectString(fmt.Sprintf("replaceall %s %s", "foo", "test_string"))
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+
+       injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl)
+
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       assert.Equal(t, srTest2, string(data))
+
+       injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl)
+       injectString(fmt.Sprintf("replace %s %s", "string", "foo"))
+       injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone)
+       injectString("ynyny")
+       injectKey(tcell.KeyEscape, 0, tcell.ModNone)
+
+       injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl)
+
+       data, err = ioutil.ReadFile(file)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+
+       assert.Equal(t, srTest3, string(data))
+}
+
+func TestMultiCursor(t *testing.T) {
+       // TODO
+}
+
+func TestSettingsPersistence(t *testing.T) {
+       // TODO
+}
+
+// more tests (rendering, tabs, plugins)?
diff --git a/go.mod b/go.mod
index a34c103aa793a7ca982507a1eb57315d153e8ebf..9b6490f957ba3ac3c1d5de182eb64519070a4e59 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
        github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5
        github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
        github.com/zyedidia/pty v2.0.0+incompatible // indirect
-       github.com/zyedidia/tcell v1.4.7
+       github.com/zyedidia/tcell v1.4.8
        github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
        golang.org/x/text v0.3.2
        gopkg.in/sourcemap.v1 v1.0.5 // indirect
diff --git a/go.sum b/go.sum
index 5f32d6213fbd6543c33e5def7a4af84598bd5de4..7dc11ac8e4bbfa1980f4a98cd44d440d4fa2b9d3 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -52,8 +52,8 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s
 github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
 github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpygApMFGweqw=
 github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA=
-github.com/zyedidia/tcell v1.4.7 h1:bKXRjv8RglPyOFqofzUUJkrdsLs9p9mT89W2ShFFlco=
-github.com/zyedidia/tcell v1.4.7/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
+github.com/zyedidia/tcell v1.4.8 h1:s4zYGOyCNDK4cdrgNVME0SxGizuT/oKY3OyB4Ls2Qpg=
+github.com/zyedidia/tcell v1.4.8/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
 github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
 github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
 golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
index c1999305b32afdc6c25ecc5799e59bd7a3aa5265..fd57eb9c70968bed99b35231d1e2808295651487 100644 (file)
@@ -82,7 +82,7 @@ modSearch:
                case strings.HasPrefix(k, "-"):
                        // We optionally support dashes between modifiers
                        k = k[1:]
-               case strings.HasPrefix(k, "Ctrl") && k != "Ctrl-h" && k != "CtrlH" && k != "Ctrlh":
+               case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
                        // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
                        k = k[4:]
                        modifiers |= tcell.ModCtrl
index 100081357f4f0c26ca7409dba84a3de0f1108039..bc29af26a493fe28072ccaa53feb4ad1859f8a69 100644 (file)
@@ -30,8 +30,9 @@ func DefaultBindings() map[string]string {
                "Alt-{":          "ParagraphPrevious",
                "Alt-}":          "ParagraphNext",
                "Enter":          "InsertNewline",
-               "Ctrl-h":         "Backspace",
+               "CtrlH":          "Backspace",
                "Backspace":      "Backspace",
+               "OldBackspace":   "Backspace",
                "Alt-CtrlH":      "DeleteWordLeft",
                "Alt-Backspace":  "DeleteWordLeft",
                "Tab":            "Autocomplete|IndentSelection|InsertTab",
index bb4e8e7de4960ffdf636a0f51eed7dadd49b93ac..d55179f9c82214227297663825f57f77cb264b92 100644 (file)
@@ -32,8 +32,9 @@ func DefaultBindings() map[string]string {
                "Alt-{":          "ParagraphPrevious",
                "Alt-}":          "ParagraphNext",
                "Enter":          "InsertNewline",
-               "Ctrl-h":         "Backspace",
+               "CtrlH":          "Backspace",
                "Backspace":      "Backspace",
+               "OldBackspace":   "Backspace",
                "Alt-CtrlH":      "DeleteWordLeft",
                "Alt-Backspace":  "DeleteWordLeft",
                "Tab":            "Autocomplete|IndentSelection|InsertTab",
index c5a80e724a5e6e30d0a47d212f89fe5d37ff76ec..f9661a0c11e49ff6ed5c05c8eeeff8ce3fd84403 100644 (file)
@@ -1,7 +1,7 @@
 package screen
 
 import (
-       "fmt"
+       "errors"
        "os"
        "sync"
        "unicode"
@@ -131,7 +131,7 @@ func TempStart(screenWasNil bool) {
 }
 
 // Init creates and initializes the tcell screen
-func Init() {
+func Init() error {
        drawChan = make(chan bool, 8)
 
        // Should we enable true color?
@@ -151,13 +151,10 @@ func Init() {
        var err error
        Screen, err = tcell.NewScreen()
        if err != nil {
-               fmt.Println(err)
-               fmt.Println("Fatal: Micro could not initialize a Screen.")
-               os.Exit(1)
+               return err
        }
        if err = Screen.Init(); err != nil {
-               fmt.Println(err)
-               os.Exit(1)
+               return err
        }
 
        // restore TERM
@@ -168,4 +165,30 @@ func Init() {
        if config.GetGlobalOption("mouse").(bool) {
                Screen.EnableMouse()
        }
+
+       return nil
+}
+
+// InitSimScreen initializes a simulation screen for testing purposes
+func InitSimScreen() (tcell.SimulationScreen, error) {
+       drawChan = make(chan bool, 8)
+
+       // Initilize tcell
+       var err error
+       s := tcell.NewSimulationScreen("")
+       if s == nil {
+               return nil, errors.New("Failed to get a simulation screen")
+       }
+       if err = s.Init(); err != nil {
+               return nil, err
+       }
+
+       s.SetSize(80, 24)
+       Screen = s
+
+       if config.GetGlobalOption("mouse").(bool) {
+               Screen.EnableMouse()
+       }
+
+       return s, nil
 }