diff --git a/conf/wide.json b/conf/wide.json
index 8621716..ca1a835 100644
--- a/conf/wide.json
+++ b/conf/wide.json
@@ -11,13 +11,13 @@
"RuntimeMode": "dev",
"Pwd": "{pwd}",
"Workspace": "{pwd}/data/workspace",
- "Locale": "zh_CN",
+ "Locale": "en_US",
"Users": [
{
"Name": "admin",
"Password": "admin",
"Workspace": "{pwd}/data/user_workspaces/admin",
- "Locale": "zh_CN",
+ "Locale": "en_US",
"GoFormat": "gofmt",
"LatestSessionContent": {
"FileTree": [],
diff --git a/data/user_workspaces/admin/src/gotest/example1_test.go b/data/user_workspaces/admin/src/gotest/example1_test.go
new file mode 100644
index 0000000..8c9c857
--- /dev/null
+++ b/data/user_workspaces/admin/src/gotest/example1_test.go
@@ -0,0 +1,16 @@
+package test
+
+import (
+ "testing"
+)
+
+func Test_Division_1(t *testing.T) {
+ if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
+ t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
+ } else {
+ t.Log("第一个测试通过了") //记录一些你期望记录的信息
+ }
+}
+
+func Test_Division_2(t *testing.T) {
+}
diff --git a/data/user_workspaces/admin/src/gotest/example2_test.go b/data/user_workspaces/admin/src/gotest/example2_test.go
new file mode 100644
index 0000000..ebdfe2b
--- /dev/null
+++ b/data/user_workspaces/admin/src/gotest/example2_test.go
@@ -0,0 +1,17 @@
+package test
+
+import (
+ "testing"
+)
+
+func Test_Division_3(t *testing.T) {
+ if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
+ t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
+ } else {
+ t.Log("第一个测试通过了") //记录一些你期望记录的信息
+ }
+}
+
+func Test_Division_4(t *testing.T) {
+ t.Error("就是不通过")
+}
diff --git a/data/user_workspaces/admin/src/gotest/test.go b/data/user_workspaces/admin/src/gotest/test.go
new file mode 100644
index 0000000..8394003
--- /dev/null
+++ b/data/user_workspaces/admin/src/gotest/test.go
@@ -0,0 +1,13 @@
+package test
+
+import (
+ "errors"
+)
+
+func Division(a, b float64) (float64, error) {
+ if b == 0 {
+ return 0, errors.New("除数不能为0")
+ }
+
+ return a / b, nil
+}
diff --git a/data/user_workspaces/admin/src/mytest/hello/main.go b/data/user_workspaces/admin/src/hello/main.go
similarity index 100%
rename from data/user_workspaces/admin/src/mytest/hello/main.go
rename to data/user_workspaces/admin/src/hello/main.go
diff --git a/data/user_workspaces/admin/src/mytest/time/index.html b/data/user_workspaces/admin/src/mytest/time/index.html
deleted file mode 100644
index 3d0944b..0000000
--- a/data/user_workspaces/admin/src/mytest/time/index.html
+++ /dev/null
@@ -1,411 +0,0 @@
-
-
-
-
-
- {{.i18n.wide}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{.i18n.file}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{.i18n.output}}
-
-
-
-
-
- {{.i18n.search}}
-
-
-
-
-
- {{.i18n.notification}}
-
-
-
-
-
-
-
-
-
- {{.i18n.isDelete}}
-
-
-
- ?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/data/user_workspaces/admin/src/mytest/time/main.go b/data/user_workspaces/admin/src/time/main.go
similarity index 89%
rename from data/user_workspaces/admin/src/mytest/time/main.go
rename to data/user_workspaces/admin/src/time/main.go
index 5cb2f19..a95a3fd 100644
--- a/data/user_workspaces/admin/src/mytest/time/main.go
+++ b/data/user_workspaces/admin/src/time/main.go
@@ -2,8 +2,8 @@ package main
import (
"fmt"
- "mytest/time/pkg"
"time"
+ "time/pkg"
)
func main() {
diff --git a/data/user_workspaces/admin/src/mytest/time/pkg/time.go b/data/user_workspaces/admin/src/time/pkg/time.go
similarity index 100%
rename from data/user_workspaces/admin/src/mytest/time/pkg/time.go
rename to data/user_workspaces/admin/src/time/pkg/time.go
diff --git a/i18n/en_US.json b/i18n/en_US.json
index bac7c64..e1083c9 100644
--- a/i18n/en_US.json
+++ b/i18n/en_US.json
@@ -7,7 +7,7 @@
"username": "Username",
"current_user": "Current User",
"password": "Password",
- "login_failed": "Login Failed",
+ "login_error": "Login Error",
"run": "Run",
"debug": "Debug",
"help": "Help",
@@ -86,13 +86,16 @@
"focus_notification": "Focus to Notification",
"start-build": "START [go build]",
"build-succ": "[go build] SUCCESS",
- "build-failed": "[go build] Failed",
+ "build-error": "[go build] ERROR",
+ "start-test": "START [go test]",
+ "test-succ": "[go test] SUCCESS",
+ "test-error": "[go test] ERROR",
"start-install": "START [go install]",
"install-succ": "[go install] SUCCESS",
- "install-failed": "[go install] Fialed",
+ "install-error": "[go install] ERROR",
"start-get": "START [go get]",
"get-succ": "[go get] SUCCESS",
- "get-failed": "[go get] Failed",
+ "get-error": "[go get] ERROR",
"check_version": "Checking update",
"new_version_available": "new version available",
"go_env": "Go",
@@ -101,5 +104,6 @@
"license": "License",
"credits": "Credits",
"uptodate": "it is up to date",
+ "test": "Test",
"colon": ": "
}
\ No newline at end of file
diff --git a/i18n/zh_CN.json b/i18n/zh_CN.json
index e66384d..0e3a466 100644
--- a/i18n/zh_CN.json
+++ b/i18n/zh_CN.json
@@ -7,7 +7,7 @@
"username": "用户名",
"current_user": "当前用户",
"password": "密码",
- "login_failed": "登录失败",
+ "login_error": "登录失败",
"run": "运行",
"debug": "调试",
"help": "帮助",
@@ -86,13 +86,16 @@
"focus_notification": "焦点切换到通知窗口",
"start-build": "开始 [go build]",
"build-succ": "[go build] 成功",
- "build-failed": "[go build] 失败",
+ "build-error": "[go build] 失败",
+ "start-test": "开始 [go test]",
+ "test-succ": "[go test] 成功",
+ "test-error": "[go test] 失败",
"start-install": "开始 [go install]",
"install-succ": "[go install] 成功",
- "install-failed": "[go install] 失败",
+ "install-error": "[go install] 失败",
"start-get": "开始 [go get]",
"get-succ": "[go get] 成功",
- "get-failed": "[go get] 失败",
+ "get-error": "[go get] 失败",
"check_version": "正在检查更新",
"new_version_available": "新版本可用",
"go_env": "Go 环境",
@@ -101,5 +104,6 @@
"license": "许可协议",
"credits": "致谢",
"uptodate": "已是最新版本",
+ "test": "测试",
"colon": ":"
}
\ No newline at end of file
diff --git a/main.go b/main.go
index c526665..e8f344a 100644
--- a/main.go
+++ b/main.go
@@ -294,6 +294,7 @@ func main() {
http.HandleFunc("/build", handlerWrapper(output.BuildHandler))
http.HandleFunc("/run", handlerWrapper(output.RunHandler))
http.HandleFunc("/stop", handlerWrapper(output.StopHandler))
+ http.HandleFunc("/go/test", handlerWrapper(output.GoTestHandler))
http.HandleFunc("/go/get", handlerWrapper(output.GoGetHandler))
http.HandleFunc("/go/install", handlerWrapper(output.GoInstallHandler))
http.HandleFunc("/output/ws", handlerWrapper(output.WSHandler))
diff --git a/output/outputs.go b/output/outputs.go
index c1038ed..e238d92 100644
--- a/output/outputs.go
+++ b/output/outputs.go
@@ -56,11 +56,9 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{"succ": true}
defer util.RetJSON(w, r, data)
- decoder := json.NewDecoder(r.Body)
-
var args map[string]interface{}
- if err := decoder.Decode(&args); err != nil {
+ if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
glog.Error(err)
data["succ"] = false
@@ -188,11 +186,9 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
username := httpSession.Values["username"].(string)
locale := conf.Wide.GetUser(username).Locale
- decoder := json.NewDecoder(r.Body)
-
var args map[string]interface{}
- if err := decoder.Decode(&args); err != nil {
+ if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
glog.Error(err)
data["succ"] = false
@@ -330,7 +326,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
} else { // 构建失败
// 解析错误信息,返回给编辑器 gutter lint
errOut := string(buf)
- channelRet["output"] = "" + i18n.Get(locale, "build-failed").(string) + "\n" + errOut
+ channelRet["output"] = "" + i18n.Get(locale, "build-error").(string) + "\n" + errOut
lines := strings.Split(errOut, "\n")
@@ -390,8 +386,8 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
}(rand.Int())
}
-// go install.
-func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
+// go test.
+func GoTestHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{"succ": true}
defer util.RetJSON(w, r, data)
@@ -399,11 +395,9 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
username := httpSession.Values["username"].(string)
locale := conf.Wide.GetUser(username).Locale
- decoder := json.NewDecoder(r.Body)
-
var args map[string]interface{}
- if err := decoder.Decode(&args); err != nil {
+ if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
glog.Error(err)
data["succ"] = false
@@ -415,8 +409,12 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
filePath := args["file"].(string)
curDir := filePath[:strings.LastIndex(filePath, conf.PathSeparator)]
- fout, err := os.Create(filePath)
+ cmd := exec.Command("go", "test", "-v")
+ cmd.Dir = curDir
+ setCmdEnv(cmd, username)
+
+ stdout, err := cmd.StdoutPipe()
if nil != err {
glog.Error(err)
data["succ"] = false
@@ -424,17 +422,108 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
return
}
- code := args["code"].(string)
-
- fout.WriteString(code)
-
- if err := fout.Close(); nil != err {
+ stderr, err := cmd.StderrPipe()
+ if nil != err {
glog.Error(err)
data["succ"] = false
return
}
+ if !data["succ"].(bool) {
+ return
+ }
+
+ channelRet := map[string]interface{}{}
+
+ if nil != session.OutputWS[sid] {
+ // 在前端 output 中显示“开始 go test
+
+ channelRet["output"] = "" + i18n.Get(locale, "start-test").(string) + "\n"
+ channelRet["cmd"] = "start-test"
+
+ wsChannel := session.OutputWS[sid]
+
+ err := wsChannel.Conn.WriteJSON(&channelRet)
+ if nil != err {
+ glog.Error(err)
+ return
+ }
+
+ // 更新通道最近使用时间
+ wsChannel.Time = time.Now()
+ }
+
+ reader := bufio.NewReader(io.MultiReader(stdout, stderr))
+
+ if err := cmd.Start(); nil != err {
+ glog.Error(err)
+ data["succ"] = false
+
+ return
+ }
+
+ go func(runningId int) {
+ defer util.Recover()
+
+ glog.V(3).Infof("Session [%s] is running [go test] [runningId=%d]", sid, runningId)
+
+ channelRet := map[string]interface{}{}
+ channelRet["cmd"] = "go test"
+
+ // 一次性读取
+ buf, _ := ioutil.ReadAll(reader)
+
+ // 同步点,等待 go test 执行完成
+ cmd.Wait()
+
+ if !cmd.ProcessState.Success() {
+ glog.V(3).Infof("Session [%s] 's running [go test] [runningId=%d] has done (with error)", sid, runningId)
+
+ channelRet["output"] = "" + i18n.Get(locale, "test-error").(string) + "\n" + string(buf)
+ } else {
+ glog.V(3).Infof("Session [%s] 's running [go test] [runningId=%d] has done", sid, runningId)
+
+ channelRet["output"] = "" + i18n.Get(locale, "test-succ").(string) + "\n" + string(buf)
+ }
+
+ if nil != session.OutputWS[sid] {
+ wsChannel := session.OutputWS[sid]
+
+ err := wsChannel.Conn.WriteJSON(&channelRet)
+ if nil != err {
+ glog.Error(err)
+ }
+
+ // 更新通道最近使用时间
+ wsChannel.Time = time.Now()
+ }
+ }(rand.Int())
+}
+
+// go install.
+func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
+ data := map[string]interface{}{"succ": true}
+ defer util.RetJSON(w, r, data)
+
+ httpSession, _ := session.HTTPSession.Get(r, "wide-session")
+ username := httpSession.Values["username"].(string)
+ locale := conf.Wide.GetUser(username).Locale
+
+ var args map[string]interface{}
+
+ if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
+ glog.Error(err)
+ data["succ"] = false
+
+ return
+ }
+
+ sid := args["sid"].(string)
+
+ filePath := args["file"].(string)
+ curDir := filePath[:strings.LastIndex(filePath, conf.PathSeparator)]
+
cmd := exec.Command("go", "install")
cmd.Dir = curDir
@@ -547,7 +636,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
channelRet["lints"] = lints
- channelRet["output"] = "" + i18n.Get(locale, "install-failed").(string) + "\n" + errOut
+ channelRet["output"] = "" + i18n.Get(locale, "install-error").(string) + "\n" + errOut
} else {
channelRet["output"] = "" + i18n.Get(locale, "install-succ").(string) + "\n"
}
@@ -577,11 +666,9 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
username := httpSession.Values["username"].(string)
locale := conf.Wide.GetUser(username).Locale
- decoder := json.NewDecoder(r.Body)
-
var args map[string]interface{}
- if err := decoder.Decode(&args); err != nil {
+ if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
glog.Error(err)
data["succ"] = false
@@ -662,7 +749,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
if 0 != len(buf) {
glog.V(3).Infof("Session [%s] 's running [go get] [runningId=%d] has done (with error)", sid, runningId)
- channelRet["output"] = "" + i18n.Get(locale, "get-failed").(string) + "\n" + string(buf)
+ channelRet["output"] = "" + i18n.Get(locale, "get-error").(string) + "\n" + string(buf)
} else {
glog.V(3).Infof("Session [%s] 's running [go get] [runningId=%d] has done", sid, runningId)
diff --git a/static/css/wide.css b/static/css/wide.css
index d7eb7c8..5d8f89b 100644
--- a/static/css/wide.css
+++ b/static/css/wide.css
@@ -271,6 +271,7 @@
}
.bottom-window-group .output {
+ font-family: Consolas, Courier New, monospace;
padding: 0 5px;
line-height: 16px;
font-size: 12px;
@@ -281,20 +282,23 @@
}
.bottom-window-group .output .start-build,
+.bottom-window-group .output .start-test,
.bottom-window-group .output .start-install,
.bottom-window-group .output .start-get {
color: #999;
}
-.bottom-window-group .output .build-succ,
+.bottom-window-group .output .build-succ,
+.bottom-window-group .output .test-succ,
.bottom-window-group .output .install-succ,
.bottom-window-group .output .get-succ {
color: rgb(0,153,0);
}
-.bottom-window-group .output .build-failed,
-.bottom-window-group .output .install-failed,
-.bottom-window-group .output .get-failed {
+.bottom-window-group .output .build-error,
+.bottom-window-group .output .test-error,
+.bottom-window-group .output .install-error,
+.bottom-window-group .output .get-error {
color: red;
}
diff --git a/static/js/editors.js b/static/js/editors.js
index 031b577..d2e13a0 100644
--- a/static/js/editors.js
+++ b/static/js/editors.js
@@ -24,7 +24,7 @@ var editors = {
wide.curEditor.focus();
},
removeAfter: function (id, nextId) {
- if (id === 'startPage') {
+ if (id === 'startPage') { // 当前关闭的 tab 是起始页
return false;
}
@@ -35,6 +35,10 @@ var editors = {
break;
}
}
+ if (editors.data.length === 0) { // 起始页可能存在,所以用编辑器数据判断
+ menu.disabled(['save-all', 'close-all', 'build', 'run', 'go-test', 'go-get', 'go-install']);
+ $(".toolbars").hide();
+ }
if (!nextId) {
// 不存在打开的编辑器
@@ -43,9 +47,6 @@ var editors = {
wide.curNode = undefined;
wide.curEditor = undefined;
-
- menu.disabled(['save-all', 'close-all', 'run', 'go-get', 'go-install']);
- $(".toolbars").hide();
return false;
}
@@ -440,7 +441,7 @@ var editors = {
content: ''
});
- menu.undisabled(['save-all', 'close-all', 'run', 'go-get', 'go-install']);
+ menu.undisabled(['save-all', 'close-all', 'build', 'run', 'go-test', 'go-get', 'go-install']);
var rulers = [];
rulers.push({color: "#ccc", column: 120, lineStyle: "dashed"});
diff --git a/static/js/wide.js b/static/js/wide.js
index f4a3d48..751174b 100644
--- a/static/js/wide.js
+++ b/static/js/wide.js
@@ -351,11 +351,13 @@ var wide = {
break;
case 'start-build':
+ case 'start-test':
case 'start-install':
case 'start-get':
wide.fillOutput(data.output);
break;
+ case 'go test':
case 'go install':
case 'go get':
wide.fillOutput($('.bottom-window-group .output > div').html() + data.output);
@@ -541,6 +543,10 @@ var wide = {
if (!currentPath) {
return false;
}
+
+ if ($(".menu li.build").hasClass("disabled")) {
+ return false;
+ }
var request = newWideRequest();
request.file = currentPath;
@@ -600,6 +606,36 @@ var wide = {
}
});
},
+ // 测试.
+ test: function () {
+ wide.saveAllFiles();
+
+ var currentPath = editors.getCurrentPath();
+ if (!currentPath) {
+ return false;
+ }
+
+ if ($(".menu li.test").hasClass("disabled")) {
+ return false;
+ }
+
+ var request = newWideRequest();
+ request.file = currentPath;
+
+ $.ajax({
+ type: 'POST',
+ url: '/go/test',
+ data: JSON.stringify(request),
+ dataType: "json",
+ beforeSend: function (data) {
+ $('.bottom-window-group .output > div').text('');
+ wide.bottomWindowTab.setCurrent("output");
+ windows.flowBottom();
+ },
+ success: function (data) {
+ }
+ });
+ },
goget: function () {
wide.saveAllFiles();
@@ -643,7 +679,6 @@ var wide = {
var request = newWideRequest();
request.file = currentPath;
- request.code = wide.curEditor.getValue();
$.ajax({
type: 'POST',
diff --git a/views/index.html b/views/index.html
index b49e5f1..597c2e4 100644
--- a/views/index.html
+++ b/views/index.html
@@ -46,13 +46,17 @@
{{.i18n.run}}
- -
+
-
{{.i18n.build}}
-
{{.i18n.build_n_run}}
+ -
+ {{.i18n.test}}
+
+
-
{{.i18n.goget}}
diff --git a/views/login.html b/views/login.html
index 5c2fc7e..bf210d7 100644
--- a/views/login.html
+++ b/views/login.html
@@ -58,7 +58,7 @@
dataType: "json",
success: function (data) {
if (!data.succ) {
- $("#msg").text('{{.i18n.login_failed}}').show();
+ $("#msg").text('{{.i18n.login_error}}').show();
return;
}
@@ -70,7 +70,7 @@
$("#username").keydown(function (event) {
if (event.which === 13) {
if ($.trim($(this).val()) === "") {
- $("#msg").text("{{.i18n.login_failed}}").show();
+ $("#msg").text("{{.i18n.login_error}}").show();
} else {
$("#password").focus();
}
@@ -83,7 +83,7 @@
$("#password").keydown(function (event) {
if (event.which === 13) {
if ($.trim($(this).val()) === "") {
- $("#msg").text("{{.i18n.login_failed}}").show();
+ $("#msg").text("{{.i18n.login_error}}").show();
} else {
login();
}