是我静香不够骚,还是你胖虎开始飘~

pull/345/head
Otokaze 5 years ago
parent 529cbc5245
commit dd509fed54
  1. 3
      go.mod
  2. 49
      go.sum
  3. 4
      pkg/testing/lich/README.md
  4. 130
      pkg/testing/lich/composer.go
  5. 23
      pkg/testing/lich/composer_test.go
  6. 25
      pkg/testing/lich/docker-compose.yaml
  7. 85
      pkg/testing/lich/healthcheck.go
  8. 88
      pkg/testing/lich/model.go
  9. 154
      tool/testcli/README.MD
  10. 60
      tool/testcli/main.go
  11. 52
      tool/testgen/README.md
  12. 419
      tool/testgen/gen.go
  13. 57
      tool/testgen/main.go
  14. 193
      tool/testgen/parser.go
  15. 41
      tool/testgen/templete.go
  16. 42
      tool/testgen/utils.go

@ -28,6 +28,7 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/montanaflynn/stats v0.5.0
github.com/openzipkin/zipkin-go v0.2.1
github.com/otokaze/mock v0.0.0-20190125081256-8282b7a7c7c3
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.0.0
github.com/prometheus/client_model v0.0.0-20190220174349-fd36f4220a90 // indirect
@ -38,6 +39,7 @@ require (
github.com/shirou/gopsutil v2.19.6+incompatible
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726
github.com/sirupsen/logrus v1.4.2
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
github.com/stretchr/testify v1.3.0
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa
@ -49,6 +51,7 @@ require (
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
golang.org/x/time v0.0.0-20190513212739-9d24e82272b4 // indirect
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b
google.golang.org/appengine v1.6.1 // indirect
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610
google.golang.org/grpc v1.22.0

@ -4,16 +4,20 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d h1:VWP4o43LuzNbykZJzMUv5b9DWLgn0sn3GUj3RUyWMMQ=
github.com/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aristanetworks/goarista v0.0.0-20190712234253-ed1100a1c015 h1:7ABPr1+uJdqESAdlVevnc/2FJGiC/K3uMg1JiELeF+0=
github.com/aristanetworks/goarista v0.0.0-20190712234253-ed1100a1c015/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190531201743-edce55837238 h1:uNljlOxtOHrPnRoPPx+JanqjAGZpNiqAGVBfGskd/pg=
github.com/cockroachdb/datadriven v0.0.0-20190531201743-edce55837238/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@ -21,14 +25,18 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBt
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM=
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
@ -43,6 +51,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
@ -57,10 +66,13 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -68,33 +80,47 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.4.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.4 h1:5xLhQjsk4zqPf9EHCrja2qFZMx+yBqkO3XgJ14bNnU0=
github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.0.0/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
@ -109,7 +135,9 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk=
github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
@ -121,6 +149,10 @@ github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/otokaze/mock v0.0.0-20190125081256-8282b7a7c7c3 h1:zjmNboC3QFuMdJSaZJ7Qvi3HUxWXPdj7wb3rc4jH5HI=
github.com/otokaze/mock v0.0.0-20190125081256-8282b7a7c7c3/go.mod h1:pLR8n2aimFxvvDJ6n8JuQWthMGezCYMjuhlaTjPTZf0=
github.com/otokaze/mock v1.1.1 h1:mQSWY25OMRIdBBuJO3AJ5CZ77WK6QtY0leU4qKGj4ok=
github.com/otokaze/mock v1.1.1/go.mod h1:pLR8n2aimFxvvDJ6n8JuQWthMGezCYMjuhlaTjPTZf0=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@ -147,6 +179,7 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20190806203942-babf20351dd7e3ac320adedbbe5eb311aec8763c h1:eED6LswgZ3TfAl9fb+L2TfdSlXpYdg21iWZMdHuoSks=
github.com/remyoudompheng/bigfft v0.0.0-20190806203942-babf20351dd7e3ac320adedbbe5eb311aec8763c/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY=
@ -160,8 +193,14 @@ github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -170,12 +209,15 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa h1:V/ABqiqsgqmpoIcLDSpJ1KqPfbxRmkcfET5+BRy9ctM=
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa/go.mod h1:3HfLQly3YNLGxNv/2YOfmz30vcjG9hbuME1GpxoLlGs=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20190720005121-fe86a786a4c3 h1:LlCFU/KJ9P/8QKB73kkd1z/zbm7ZJ2V4HEgEHI3N7gk=
go.etcd.io/etcd v0.0.0-20190720005121-fe86a786a4c3/go.mod h1:N0RPWo9FXJYZQI4BTkDtQylrstIigYHeR18ONnyTufk=
@ -191,6 +233,7 @@ golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -236,11 +279,14 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b h1:mSUCVIwDx4hfXJfWsOPfdzEHxzb2Xjl6BQ8YgPnazQA=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180608181217-32ee49c4dd80/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@ -254,10 +300,12 @@ google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
@ -269,4 +317,5 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

@ -0,0 +1,4 @@
## testing/lich 运行环境构建
基于 docker-compose 实现跨平台跨语言环境的容器依赖管理方案,以解决运行ut场景下的 (mysql, redis, mc)容器依赖问题。
使用说明参见:https://github.com/bilibili/kratos/tree/master/tool/testcli/README.md

@ -0,0 +1,130 @@
package lich
import (
"bytes"
"crypto/md5"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"time"
)
var (
retry int
yamlPath string
pathHash string
services map[string]*Container
)
func init() {
flag.StringVar(&yamlPath, "f", "docker-compose.yaml", "composer yaml path.")
}
// Setup setup UT related environment dependence for everything.
func Setup() (err error) {
if _, err = os.Stat(yamlPath); os.IsNotExist(err) {
log.Println("composer yaml is not exist!", yamlPath)
return
}
if yamlPath, err = filepath.Abs(yamlPath); err != nil {
log.Printf("filepath.Abs(%s) error(%v)", yamlPath, err)
return
}
pathHash = fmt.Sprintf("%x", md5.Sum([]byte(yamlPath)))[:9]
var args = []string{"-f", yamlPath, "-p", pathHash, "up", "-d"}
if err = exec.Command("docker-compose", args...).Run(); err != nil {
log.Printf("exec.Command(docker-composer) args(%v) error(%v)", args, err)
Teardown()
return
}
// 拿到yaml文件中的服务名,同时通过服务名获取到启动的容器ID
if _, err = getServices(); err != nil {
Teardown()
return
}
// 通过容器ID检测容器的状态,包括容器服务的状态
if _, err = checkServices(); err != nil {
Teardown()
return
}
return
}
// Teardown unsetup all environment dependence.
func Teardown() (err error) {
if _, err = os.Stat(yamlPath); os.IsNotExist(err) {
log.Println("composer yaml is not exist!")
return
}
if yamlPath, err = filepath.Abs(yamlPath); err != nil {
log.Printf("filepath.Abs(%s) error(%v)", yamlPath, err)
return
}
pathHash = fmt.Sprintf("%x", md5.Sum([]byte(yamlPath)))[:9]
args := []string{"-f", yamlPath, "-p", pathHash, "down"}
if output, err := exec.Command("docker-compose", args...).CombinedOutput(); err != nil {
log.Fatalf("exec.Command(docker-composer) args(%v) stdout(%s) error(%v)", args, string(output), err)
return err
}
return
}
func getServices() (output []byte, err error) {
var args = []string{"-f", yamlPath, "-p", pathHash, "config", "--services"}
if output, err = exec.Command("docker-compose", args...).CombinedOutput(); err != nil {
log.Printf("exec.Command(docker-composer) args(%v) stdout(%s) error(%v)", args, string(output), err)
return
}
services = make(map[string]*Container)
output = bytes.TrimSpace(output)
for _, svr := range bytes.Split(output, []byte("\n")) {
args = []string{"-f", yamlPath, "-p", pathHash, "ps", "-a", "-q", string(svr)}
if output, err = exec.Command("docker-compose", args...).CombinedOutput(); err != nil {
log.Printf("exec.Command(docker-composer) args(%v) stdout(%s) error(%v)", args, string(output), err)
return
}
var id = string(bytes.TrimSpace(output))
args = []string{"inspect", id, "--format", "'{{json .}}'"}
if output, err = exec.Command("docker", args...).CombinedOutput(); err != nil {
log.Printf("exec.Command(docker) args(%v) stdout(%s) error(%v)", args, string(output), err)
return
}
if output = bytes.TrimSpace(output); bytes.Equal(output, []byte("")) {
err = fmt.Errorf("service: %s | container: %s fails to launch", svr, id)
log.Printf("exec.Command(docker) args(%v) error(%v)", args, err)
return
}
var c = &Container{}
if err = json.Unmarshal(bytes.Trim(output, "'"), c); err != nil {
log.Printf("json.Unmarshal(%s) error(%v)", string(output), err)
return
}
services[string(svr)] = c
}
return
}
func checkServices() (output []byte, err error) {
defer func() {
if err != nil && retry < 4 {
retry++
getServices()
time.Sleep(time.Second * 5)
output, err = checkServices()
return
}
retry = 0
}()
for svr, c := range services {
if err = c.Healthcheck(); err != nil {
log.Printf("healthcheck(%s) error(%v) retrying %d times...", svr, err, 5-retry)
return
}
// TODO About container check and more...
}
return
}

@ -0,0 +1,23 @@
package lich
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestComposer(t *testing.T) {
convey.Convey("Composer testing....", t, func(convCtx convey.C) {
convCtx.Convey("When Setup everything goes positive", func(convCtx convey.C) {
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(Setup(), convey.ShouldBeNil)
})
})
convCtx.Convey("When UnSetup everything goes positive", func(convCtx convey.C) {
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(Teardown(), convey.ShouldBeNil)
})
})
})
}

@ -0,0 +1,25 @@
version: "3.7"
services:
db:
image: mysql:5.6
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=root
volumes:
- .:/docker-entrypoint-initdb.d
command: [
'--character-set-server=utf8',
'--collation-server=utf8_unicode_ci'
]
redis:
image: redis
ports:
- 6379:6379
memcached:
image: memcached
ports:
- 11211:11211

@ -0,0 +1,85 @@
package lich
import (
"fmt"
"log"
"net"
"strconv"
"strings"
"database/sql"
// Register go-sql-driver stuff
_ "github.com/go-sql-driver/mysql"
)
var healthchecks = map[string]func(*Container) error{"mysql": checkMysql, "mariadb": checkMysql}
// Healthcheck check container health.
func (c *Container) Healthcheck() (err error) {
if status, health := c.State.Status, c.State.Health.Status; !c.State.Running || (health != "" && health != "healthy") {
err = fmt.Errorf("service: %s | container: %s not running", c.GetImage(), c.GetID())
log.Printf("docker status(%s) health(%s) error(%v)", status, health, err)
return
}
if check, ok := healthchecks[c.GetImage()]; ok {
err = check(c)
return
}
for proto, ports := range c.NetworkSettings.Ports {
if id := c.GetID(); !strings.Contains(proto, "tcp") {
log.Printf("container: %s proto(%s) unsupported.", id, proto)
continue
}
for _, pulish := range ports {
var (
ip = net.ParseIP(pulish.HostIP)
port, _ = strconv.Atoi(pulish.HostPort)
tcpAddr = &net.TCPAddr{IP: ip, Port: port}
tcpConn *net.TCPConn
)
if tcpConn, err = net.DialTCP("tcp", nil, tcpAddr); err != nil {
log.Printf("net.DialTCP(%s:%s) error(%v)", pulish.HostIP, pulish.HostPort, err)
return
}
tcpConn.Close()
}
}
return
}
func checkMysql(c *Container) (err error) {
var ip, port, user, passwd string
for _, env := range c.Config.Env {
splits := strings.Split(env, "=")
if strings.Contains(splits[0], "MYSQL_ROOT_PASSWORD") {
user, passwd = "root", splits[1]
continue
}
if strings.Contains(splits[0], "MYSQL_ALLOW_EMPTY_PASSWORD") {
user, passwd = "root", ""
continue
}
if strings.Contains(splits[0], "MYSQL_USER") {
user = splits[1]
continue
}
if strings.Contains(splits[0], "MYSQL_PASSWORD") {
passwd = splits[1]
continue
}
}
var db *sql.DB
if ports, ok := c.NetworkSettings.Ports["3306/tcp"]; ok {
ip, port = ports[0].HostIP, ports[0].HostPort
}
var dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/", user, passwd, ip, port)
if db, err = sql.Open("mysql", dsn); err != nil {
log.Printf("sql.Open(mysql) dsn(%s) error(%v)", dsn, err)
return
}
if err = db.Ping(); err != nil {
log.Printf("ping(db) dsn(%s) error(%v)", dsn, err)
}
defer db.Close()
return
}

@ -0,0 +1,88 @@
package lich
import (
"strings"
"time"
)
// Container docker inspect resp.
type Container struct {
ID string `json:"Id"`
Created time.Time `json:"Created"`
Path string `json:"Path"`
Args []string `json:"Args"`
State struct {
Status string `json:"Status"`
Running bool `json:"Running"`
Paused bool `json:"Paused"`
Restarting bool `json:"Restarting"`
OOMKilled bool `json:"OOMKilled"`
Dead bool `json:"Dead"`
Pid int `json:"Pid"`
ExitCode int `json:"ExitCode"`
Error string `json:"Error"`
StartedAt time.Time `json:"StartedAt"`
FinishedAt time.Time `json:"FinishedAt"`
Health struct {
Status string `json:"Status"`
FailingStreak int `json:"FailingStreak"`
Log []struct {
Start time.Time `json:"Start"`
End time.Time `json:"End"`
ExitCode int `json:"ExitCode"`
Output string `json:"Output"`
} `json:"Log"`
} `json:"Health"`
} `json:"State"`
Config struct {
Hostname string `json:"Hostname"`
Domainname string `json:"Domainname"`
User string `json:"User"`
Tty bool `json:"Tty"`
OpenStdin bool `json:"OpenStdin"`
StdinOnce bool `json:"StdinOnce"`
Env []string `json:"Env"`
Cmd []string `json:"Cmd"`
Image string `json:"Image"`
WorkingDir string `json:"WorkingDir"`
Entrypoint []string `json:"Entrypoint"`
} `json:"Config"`
Image string `json:"Image"`
ResolvConfPath string `json:"ResolvConfPath"`
HostnamePath string `json:"HostnamePath"`
HostsPath string `json:"HostsPath"`
LogPath string `json:"LogPath"`
Name string `json:"Name"`
RestartCount int `json:"RestartCount"`
Driver string `json:"Driver"`
Platform string `json:"Platform"`
MountLabel string `json:"MountLabel"`
ProcessLabel string `json:"ProcessLabel"`
AppArmorProfile string `json:"AppArmorProfile"`
NetworkSettings struct {
Bridge string `json:"Bridge"`
SandboxID string `json:"SandboxID"`
HairpinMode bool `json:"HairpinMode"`
Ports map[string][]struct {
HostIP string `json:"HostIp"`
HostPort string `json:"HostPort"`
} `json:"Ports"`
} `json:"NetworkSettings"`
}
// GetImage get image name at container
func (c *Container) GetImage() (image string) {
image = c.Config.Image
if images := strings.Split(image, ":"); len(images) > 0 {
image = images[0]
}
return
}
// GetID get id at container
func (c *Container) GetID() (id string) {
if id = c.ID; len(id) > 9 {
id = id[0:9]
}
return
}

@ -0,0 +1,154 @@
## testcli UT运行环境构建工具
基于 docker-compose 实现跨平台跨语言环境的容器依赖管理方案,以解决运行ut场景下的 (mysql, redis, mc)容器依赖问题。
*这个是testing/lich的二进制工具版本(Go请直接使用库版本:github.com/bilibili/kratos/pkg/testing/lich)*
### 功能和特性
- 自动读取 test 目录下的 yaml 并启动依赖
- 自动导入 test 目录下的 DB 初始化 SQL
- 提供特定容器内的 healthcheck (mysql, mc, redis)
- 提供一站式解决 UT 服务依赖的工具版本 (testcli)
### 编译安装
*使用本工具/库需要前置安装好 docker&docker-compose*
#### Method 1. With go get
```shell
go get -u github.com/bilibili/kratos/tool/testcli
$GOPATH/bin/testcli -h
```
#### Method 2. Build with Go
```shell
cd github.com/bilibili/kratos/tool/testcli
go build -o $GOPATH/bin/testcli
$GOPATH/bin/testcli -h
```
#### Method 3. Import with Kratos pkg
```Go
import "github.com/bilibili/kratos/pkg/testing/lich"
```
### 构建数据
#### Step 1. create docker-compose.yml
创建依赖服务的 docker-compose.yml,并把它放在项目路径下的 test 文件夹下面。例如:
```shell
mkdir -p $YOUR_PROJECT/test
```
```yaml
version: "3.7"
services:
db:
image: mysql:5.6
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=root
volumes:
- .:/docker-entrypoint-initdb.d
command: [
'--character-set-server=utf8',
'--collation-server=utf8_unicode_ci'
]
redis:
image: redis
ports:
- 6379:6379
```
一般来讲,我们推荐在项目根目录创建 test 目录,里面存放描述服务的yml,以及需要初始化的数据(database.sql等)。
同时也需要注意,正确的对容器内服务进行健康检测,testcli会在容器的health状态执行UT,其实我们也内置了针对几个较为通用镜像(mysql mariadb mc redis)的健康检测,也就是不写也没事(^^;;
#### Step 2. export database.sql
构造初始化的数据(database.sql等),当然也把它也在 test 文件夹里。
```sql
CREATE DATABASE IF NOT EXISTS `YOUR_DATABASE_NAME`;
SET NAMES 'utf8';
USE `YOUR_DATABASE_NAME`;
CREATE TABLE IF NOT EXISTS `YOUR_TABLE_NAME` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='YOUR_TABLE_NAME';
```
这里需要注意,在创建库/表的时候尽量加上 IF NOT EXISTS,以给予一定程度的容错,以及 SET NAMES 'utf8'; 用于解决客户端连接乱码问题。
#### Step 3. change your project mysql config
```toml
[mysql]
addr = "127.0.0.1:3306"
dsn = "root:root@tcp(127.0.0.1:3306)/YOUR_DATABASE?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
active = 20
idle = 10
idleTimeout ="1s"
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
```
在 *Step 1* 我们以及指定了服务对外暴露的端口为3306(这当然也可以是你指定的任何值),那理所应当的我们也要修改项目连接数据库的配置~
Great! 至此你已经完成了运行所需要用到的数据配置,接下来就来运行它。
### 运行
开头也说过本工具支持两种运行方式:testcli 二进制工具版本和 go package 源码包,业务方可以根据需求场景进行选择。
#### Method 1. With testcli tool
*已支持的 flag: -f,--nodown,down,run*
- -f,指定 docker-compose.yaml 文件路径,默认为当前目录下。
- --nodown,指定是否在UT执行完成后保留容器,以供下次复用。
- down,teardown 销毁当前项目下这个 compose 文件产生的容器。
- run,运行你当前语言的单测执行命令(如:golang为 go test -v ./)
example:
```shell
testcli -f ../../test/docker-compose.yaml run go test -v ./
```
#### Method 2. Import with Kratos pkg
- Step1. 在 Dao|Service 层中的 TestMain 单测主入口中,import "go-common/library/testing/lich" 引入testcli工具的go库版本。
- Step2. 使用 flag.Set("f", "../../test/docker-compose.yaml") 指定 docker-compose.yaml 文件的路径。
- Step3. 在 flag.Parse() 后即可使用 lich.Setup() 安装依赖&初始化数据(注意测试用例执行结束后 lich.Teardown() 回收下~)
- Step4. 运行 `go test -v ./ `看看效果吧~
example:
```Go
package dao
import (
"flag"
"os"
"strings"
"testing"
"github.com/bilibili/kratos/pkg/conf/paladin"
"github.com/bilibili/kratos/pkg/testing/lich"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
flag.Set("conf", "../../configs")
flag.Set("f", "../../test/docker-compose.yaml")
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
if err := lich.Setup(); err != nil {
panic(err)
}
defer lich.Teardown()
d = New()
if code := m.Run(); code != 0 {
lich.Teardown()
os.Exit(code)
}
}
```
## 注意
因为启动mysql容器较为缓慢,健康检测的机制会重试3次,每次暂留5秒钟,基本在10s内mysql就能从creating到服务正常启动!
当然你也可以在使用 testcli 时加上 --nodown,使其不用每次跑都新建容器,只在第一次跑的时候会初始化容器,后面都进行复用,这样速度会快很多。
成功启动后就欢乐奔放的玩耍吧~ Good Lucky!

@ -0,0 +1,60 @@
package main
import (
"flag"
"os"
"os/exec"
"strings"
"github.com/bilibili/kratos/pkg/testing/lich"
)
var (
noDown bool
)
func init() {
flag.BoolVar(&noDown, "nodown", false, "containers are not recycled.")
}
func parseArgs() (flags map[string]string) {
flags = make(map[string]string)
for idx, arg := range os.Args {
if idx == 0 {
continue
}
if arg == "down" {
flags["down"] = ""
return
}
if cmds := os.Args[idx+1:]; arg == "run" {
flags["run"] = strings.Join(cmds, " ")
return
}
}
return
}
func main() {
flag.Parse()
flags := parseArgs()
if _, ok := flags["down"]; ok {
lich.Teardown()
return
}
if cmd, ok := flags["run"]; !ok || cmd == "" {
panic("Your need 'run' flag assign to be run commands.")
}
if err := lich.Setup(); err != nil {
panic(err)
}
if !noDown {
defer lich.Teardown()
}
cmds := strings.Split(flags["run"], " ")
cmd := exec.Command(cmds[0], cmds[1:]...)
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}

@ -0,0 +1,52 @@
## testgen UT代码自动生成器
解放你的双手,让你的UT一步到位!
### 功能和特性
- 支持生成 Dao|Service 层UT代码功能(每个方法包含一个正向用例)
- 支持生成 Dao|Service 层测试入口文件dao_test.go, service_test.go(用于控制初始化,控制测试流程等)
- 支持生成Mock代码(使用GoMock框架)
- 支持选择不同模式生成不同代码(使用"–m mode"指定)
- 生成单元测试代码时,同时支持传入目录或文件
- 支持指定方法追加生成测试用例(使用"–func funcName"指定)
### 编译安装
#### Method 1. With go get
```shell
go get -u github.com/bilibili/kratos/tool/testgen
$GOPATH/bin/testgen -h
```
#### Method 2. Build with Go
```shell
cd github.com/bilibili/kratos/tool/testgen
go build -o $GOPATH/bin/testgen
$GOPATH/bin/testgen -h
```
### 运行
#### 生成Dao/Service层单元UT
```shell
$GOPATH/bin/testgen YOUR_PROJECT/dao # default mode
$GOPATH/bin/testgen --m test path/to/your/pkg
$GOPATH/bin/testgen --func functionName path/to/your/pkg
```
#### 生成接口类型
```shell
$GOPATH/bin/testgen --m interface YOUR_PROJECT/dao #当前仅支持传目录,如目录包含子目录也会做处理
```
#### 生成Mock代码
```shell
$GOPATH/bin/testgen --m mock YOUR_PROJECT/dao #仅传入包路径即可
```
#### 生成Monkey代码
```shell
$GOPATH/bin/testgen --m monkey yourCodeDirPath #仅传入包路径即可
```
### 赋诗一首
```
莫生气 莫生气
代码辣鸡非我意
自己动手分田地
谈笑风生活长命
```

@ -0,0 +1,419 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/otokaze/mock/mockgen"
"github.com/otokaze/mock/mockgen/model"
)
func genTest(parses []*parse) (err error) {
for _, p := range parses {
switch {
case strings.HasSuffix(p.Path, "_mock.go") ||
strings.HasSuffix(p.Path, ".intf.go"):
continue
case strings.HasSuffix(p.Path, "dao.go") ||
strings.HasSuffix(p.Path, "service.go"):
err = p.genTestMain()
default:
err = p.genUTTest()
}
if err != nil {
break
}
}
return
}
func (p *parse) genUTTest() (err error) {
var (
buffer bytes.Buffer
impts = strings.Join([]string{
`"context"`,
`"testing"`,
`. "github.com/smartystreets/goconvey/convey"`,
}, "\n\t")
content []byte
)
filename := strings.Replace(p.Path, ".go", "_test.go", -1)
if _, err = os.Stat(filename); (_func == "" && err == nil) ||
(err != nil && os.IsExist(err)) {
err = nil
return
}
for _, impt := range p.Imports {
impts += "\n\t\"" + impt.V + "\""
}
if _func == "" {
buffer.WriteString(fmt.Sprintf(tpPackage, p.Package))
buffer.WriteString(fmt.Sprintf(tpImport, impts))
}
for _, parseFunc := range p.Funcs {
if _func != "" && _func != parseFunc.Name {
continue
}
var (
methodK string
tpVars string
vars []string
val []string
notice = "Then "
reset string
)
if method := ConvertMethod(p.Path); method != "" {
methodK = method + "."
}
tpTestFuncs := fmt.Sprintf(tpTestFunc, strings.Title(p.Package), parseFunc.Name, "", parseFunc.Name, "%s", "%s", "%s")
tpTestFuncBeCall := methodK + parseFunc.Name + "(%s)\n\t\t\tConvey(\"%s\", func() {"
if parseFunc.Result == nil {
tpTestFuncBeCall = fmt.Sprintf(tpTestFuncBeCall, "%s", "No return values")
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "%s", tpTestFuncBeCall, "%s")
}
for k, res := range parseFunc.Result {
if res.K == "" {
res.K = fmt.Sprintf("p%d", k+1)
}
var so string
if res.V == "error" {
res.K = "err"
so = fmt.Sprintf("\tSo(%s, ShouldBeNil)", res.K)
notice += "err should be nil."
} else {
so = fmt.Sprintf("\tSo(%s, ShouldNotBeNil)", res.K)
val = append(val, res.K)
}
if len(parseFunc.Result) <= k+1 {
if len(val) != 0 {
notice += strings.Join(val, ",") + " should not be nil."
}
tpTestFuncBeCall = fmt.Sprintf(tpTestFuncBeCall, "%s", notice)
res.K += " := " + tpTestFuncBeCall
} else {
res.K += ", %s"
}
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "%s", res.K+"\n\t\t\t%s", "%s")
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "%s", "%s", so, "%s")
}
if parseFunc.Params == nil {
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "%s", "", "%s")
}
for k, pType := range parseFunc.Params {
if pType.K == "" {
pType.K = fmt.Sprintf("a%d", k+1)
}
var (
init string
params = pType.K
)
switch {
case strings.HasPrefix(pType.V, "context"):
init = params + " = context.Background()"
case strings.HasPrefix(pType.V, "[]byte"):
init = params + " = " + pType.V + "(\"\")"
case strings.HasPrefix(pType.V, "[]"):
init = params + " = " + pType.V + "{}"
case strings.HasPrefix(pType.V, "int") ||
strings.HasPrefix(pType.V, "uint") ||
strings.HasPrefix(pType.V, "float") ||
strings.HasPrefix(pType.V, "double"):
init = params + " = " + pType.V + "(0)"
case strings.HasPrefix(pType.V, "string"):
init = params + " = \"\""
case strings.Contains(pType.V, "*xsql.Tx"):
init = params + ",_ = " + methodK + "BeginTran(c)"
reset += "\n\t" + params + ".Commit()"
case strings.HasPrefix(pType.V, "*"):
init = params + " = " + strings.Replace(pType.V, "*", "&", -1) + "{}"
case strings.Contains(pType.V, "chan"):
init = params + " = " + pType.V
case pType.V == "time.Time":
init = params + " = time.Now()"
case strings.Contains(pType.V, "chan"):
init = params + " = " + pType.V
default:
init = params + " " + pType.V
}
vars = append(vars, "\t\t"+init)
if len(parseFunc.Params) > k+1 {
params += ", %s"
}
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "%s", params, "%s")
}
if len(vars) > 0 {
tpVars = fmt.Sprintf(tpVar, strings.Join(vars, "\n\t"))
}
tpTestFuncs = fmt.Sprintf(tpTestFuncs, tpVars, "%s")
if reset != "" {
tpTestResets := fmt.Sprintf(tpTestReset, reset)
tpTestFuncs = fmt.Sprintf(tpTestFuncs, tpTestResets)
} else {
tpTestFuncs = fmt.Sprintf(tpTestFuncs, "")
}
buffer.WriteString(tpTestFuncs)
}
var (
file *os.File
flag = os.O_RDWR | os.O_CREATE | os.O_APPEND
)
if file, err = os.OpenFile(filename, flag, 0644); err != nil {
return
}
if _func == "" {
content, _ = GoImport(filename, buffer.Bytes())
} else {
content = buffer.Bytes()
}
if _, err = file.Write(content); err != nil {
return
}
if err = file.Close(); err != nil {
return
}
return
}
func (p *parse) genTestMain() (err error) {
var (
new bool
buffer bytes.Buffer
impts string
vars, mainFunc string
content []byte
instance, confFunc string
tomlPath = "**PUT PATH TO YOUR CONFIG FILES HERE**"
filename = strings.Replace(p.Path, ".go", "_test.go", -1)
)
if p.Imports["paladin"] != nil {
new = true
}
// if _intfMode {
// imptsList = append(imptsList, `"github.com/golang/mock/gomock"`)
// for _, field := range p.Structs {
// var hit bool
// pkgName := strings.Split(field.V, ".")[0]
// interfaceName := strings.Split(field.V, ".")[1]
// if p.Imports[pkgName] != nil {
// if hit, err = checkInterfaceMock(strings.Split(field.V, ".")[1], p.Imports[pkgName].V); err != nil {
// return
// }
// }
// if hit {
// imptsList = append(imptsList, "mock"+p.Imports[pkgName].K+" \""+p.Imports[pkgName].V+"/mock\"")
// pkgName = "mock" + strings.Title(pkgName)
// interfaceName = "Mock" + interfaceName
// varsList = append(varsList, "mock"+strings.Title(field.K)+" *"+pkgName+"."+interfaceName)
// mockStmt += "\tmock" + strings.Title(field.K) + " = " + pkgName + ".New" + interfaceName + "(mockCtrl)\n"
// newStmt += "\t\t" + field.K + ":\tmock" + strings.Title(field.K) + ",\n"
// } else {
// pkgName = subString(field.V, "*", ".")
// if p.Imports[pkgName] != nil && pkgName != "conf" {
// imptsList = append(imptsList, p.Imports[pkgName].K+" \""+p.Imports[pkgName].V+"\"")
// }
// switch {
// case strings.HasPrefix(field.V, "*conf."):
// newStmt += "\t\t" + field.K + ":\tconf.Conf,\n"
// case strings.HasPrefix(field.V, "*"):
// newStmt += "\t\t" + field.K + ":\t" + strings.Replace(field.V, "*", "&", -1) + "{},\n"
// default:
// newStmt += "\t\t" + field.K + ":\t" + field.V + ",\n"
// }
// }
// }
// mockStmt = fmt.Sprintf(_tpTestServiceMainMockStmt, mockStmt)
// newStmt = fmt.Sprintf(_tpTestServiceMainNewStmt, newStmt)
// }
if instance = ConvertMethod(p.Path); instance == "s" {
vars = strings.Join([]string{"s *Service"}, "\n\t")
mainFunc = tpTestServiceMain
} else {
vars = strings.Join([]string{"d *Dao"}, "\n\t")
mainFunc = tpTestDaoMain
}
if new {
impts = strings.Join([]string{`"os"`, `"flag"`, `"testing"`, p.Imports["paladin"].V}, "\n\t")
confFunc = fmt.Sprintf(tpTestMainNew, instance+" = New()")
} else {
impts = strings.Join(append([]string{`"os"`, `"flag"`, `"testing"`}), "\n\t")
confFunc = fmt.Sprintf(tpTestMainOld, instance+" = New(conf.Conf)")
}
if _, err := os.Stat(filename); os.IsNotExist(err) {
buffer.WriteString(fmt.Sprintf(tpPackage, p.Package))
buffer.WriteString(fmt.Sprintf(tpImport, impts))
buffer.WriteString(fmt.Sprintf(tpVar, vars))
buffer.WriteString(fmt.Sprintf(mainFunc, tomlPath, confFunc))
content, _ = GoImport(filename, buffer.Bytes())
ioutil.WriteFile(filename, content, 0644)
}
return
}
func genInterface(parses []*parse) (err error) {
var (
parse *parse
pkg = make(map[string]string)
)
for _, parse = range parses {
if strings.Contains(parse.Path, ".intf.go") {
continue
}
dirPath := filepath.Dir(parse.Path)
for _, parseFunc := range parse.Funcs {
if (parseFunc.Method == nil) ||
!(parseFunc.Name[0] >= 'A' && parseFunc.Name[0] <= 'Z') {
continue
}
var (
params string
results string
)
for k, param := range parseFunc.Params {
params += param.K + " " + param.P + param.V
if len(parseFunc.Params) > k+1 {
params += ", "
}
}
for k, res := range parseFunc.Result {
results += res.K + " " + res.P + res.V
if len(parseFunc.Result) > k+1 {
results += ", "
}
}
if len(results) != 0 {
results = "(" + results + ")"
}
pkg[dirPath] += "\t" + fmt.Sprintf(tpIntfcFunc, parseFunc.Name, params, results)
}
}
for k, v := range pkg {
var buffer bytes.Buffer
pathSplit := strings.Split(k, "/")
filename := k + "/" + pathSplit[len(pathSplit)-1] + ".intf.go"
if _, exist := os.Stat(filename); os.IsExist(exist) {
continue
}
buffer.WriteString(fmt.Sprintf(tpPackage, pathSplit[len(pathSplit)-1]))
buffer.WriteString(fmt.Sprintf(tpInterface, strings.Title(pathSplit[len(pathSplit)-1]), v))
content, _ := GoImport(filename, buffer.Bytes())
err = ioutil.WriteFile(filename, content, 0644)
}
return
}
func genMock(files ...string) (err error) {
for _, file := range files {
var pkg *model.Package
if pkg, err = mockgen.ParseFile(file); err != nil {
return
}
if len(pkg.Interfaces) == 0 {
continue
}
var mockDir = pkg.SrcDir + "/mock"
if _, err = os.Stat(mockDir); os.IsNotExist(err) {
err = nil
os.Mkdir(mockDir, 0744)
}
var mockPath = mockDir + "/" + pkg.Name + "_mock.go"
if _, exist := os.Stat(mockPath); os.IsExist(exist) {
continue
}
var g = &mockgen.Generator{Filename: file}
if err = g.Generate(pkg, "mock", mockPath); err != nil {
return
}
if err = ioutil.WriteFile(mockPath, g.Output(), 0644); err != nil {
return
}
}
return
}
func genMonkey(parses []*parse) (err error) {
var (
pkg = make(map[string]string)
)
for _, parse := range parses {
if strings.Contains(parse.Path, "monkey.go") ||
strings.Contains(parse.Path, "/mock/") {
continue
}
var (
path = strings.Split(filepath.Dir(parse.Path), "/")
pack = ConvertHump(path[len(path)-1])
refer = path[len(path)-1]
mockVar, mockType, srcDir string
)
for i := len(path) - 1; i > len(path)-4; i-- {
if path[i] == "dao" || path[i] == "service" {
srcDir = strings.Join(path[:i+1], "/")
break
}
pack = ConvertHump(path[i-1]) + pack
}
if mockVar = ConvertMethod(parse.Path); mockType == "d" {
mockType = "*" + refer + ".Dao"
} else {
mockType = "*" + refer + ".Service"
}
for _, parseFunc := range parse.Funcs {
if (parseFunc.Method == nil) || (parseFunc.Result == nil) ||
!(parseFunc.Name[0] >= 'A' && parseFunc.Name[0] <= 'Z') {
continue
}
var (
funcParams, funcResults, mockKey, mockValue, funcName string
)
funcName = pack + parseFunc.Name
for k, param := range parseFunc.Params {
funcParams += "_ " + param.V
if len(parseFunc.Params) > k+1 {
funcParams += ", "
}
}
for k, res := range parseFunc.Result {
if res.K == "" {
if res.V == "error" {
res.K = "err"
} else {
res.K = fmt.Sprintf("p%d", k+1)
}
}
mockKey += res.K
mockValue += res.V
funcResults += res.K + " " + res.P + res.V
if len(parseFunc.Result) > k+1 {
mockKey += ", "
mockValue += ", "
funcResults += ", "
}
}
pkg[srcDir+"."+refer] += fmt.Sprintf(tpMonkeyFunc, funcName, funcName, mockVar, mockType, funcResults, mockVar, parseFunc.Name, mockType, funcParams, mockValue, mockKey)
}
}
for path, content := range pkg {
var (
buffer bytes.Buffer
dir = strings.Split(path, ".")
mockDir = dir[0] + "/mock"
filename = mockDir + "/monkey_" + dir[1] + ".go"
)
if _, err = os.Stat(mockDir); os.IsNotExist(err) {
err = nil
os.Mkdir(mockDir, 0744)
}
if _, err := os.Stat(filename); os.IsExist(err) {
continue
}
buffer.WriteString(fmt.Sprintf(tpPackage, "mock"))
buffer.WriteString(content)
content, _ := GoImport(filename, buffer.Bytes())
ioutil.WriteFile(filename, content, 0644)
}
return
}

@ -0,0 +1,57 @@
package main
import (
"flag"
"fmt"
"os"
)
var (
err error
_mode, _func string
files []string
parses []*parse
)
func main() {
flag.StringVar(&_mode, "m", "test", "Generating code by Working mode. [test|interface|mock...]")
flag.StringVar(&_func, "func", "", "Generating code by function.")
flag.Parse()
if len(os.Args) == 1 {
println("Creater is a tool for generating code.\n\nUsage: creater [-m]")
flag.PrintDefaults()
return
}
if err = parseArgs(os.Args[1:], &files, 0); err != nil {
panic(err)
}
switch _mode {
case "monkey":
if parses, err = parseFile(files...); err != nil {
panic(err)
}
if err = genMonkey(parses); err != nil {
panic(err)
}
case "test":
if parses, err = parseFile(files...); err != nil {
panic(err)
}
if err = genTest(parses); err != nil {
panic(err)
}
case "interface":
if parses, err = parseFile(files...); err != nil {
panic(err)
}
if err = genInterface(parses); err != nil {
panic(err)
}
case "mock":
if err = genMock(files...); err != nil {
panic(err)
}
default:
}
fmt.Println(print)
}

@ -0,0 +1,193 @@
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
type param struct{ K, V, P string }
type parse struct {
Path string
Package string
// Imports []string
Imports map[string]*param
// Structs []*param
// Interfaces []string
Funcs []*struct {
Name string
Method, Params, Result []*param
}
}
func parseArgs(args []string, res *[]string, index int) (err error) {
if len(args) <= index {
return
}
if strings.HasPrefix(args[index], "-") {
index += 2
parseArgs(args, res, index)
return
}
var f os.FileInfo
if f, err = os.Stat(args[index]); err != nil {
return
}
if f.IsDir() {
if !strings.HasSuffix(args[index], "/") {
args[index] += "/"
}
var fs []os.FileInfo
if fs, err = ioutil.ReadDir(args[index]); err != nil {
return
}
for _, f = range fs {
path, _ := filepath.Abs(args[index] + f.Name())
args = append(args, path)
}
} else {
if strings.HasSuffix(args[index], ".go") &&
!strings.HasSuffix(args[index], "_test.go") {
*res = append(*res, args[index])
}
}
index++
return parseArgs(args, res, index)
}
func parseFile(files ...string) (parses []*parse, err error) {
for _, file := range files {
var (
astFile *ast.File
fSet = token.NewFileSet()
parse = &parse{
Imports: make(map[string]*param),
}
)
if astFile, err = parser.ParseFile(fSet, file, nil, 0); err != nil {
return
}
if astFile.Name != nil {
parse.Path = file
parse.Package = astFile.Name.Name
}
for _, decl := range astFile.Decls {
switch decl.(type) {
case *ast.GenDecl:
if specs := decl.(*ast.GenDecl).Specs; len(specs) > 0 {
parse.Imports = parseImports(specs)
}
case *ast.FuncDecl:
var (
dec = decl.(*ast.FuncDecl)
parseFunc = &struct {
Name string
Method, Params, Result []*param
}{Name: dec.Name.Name}
)
if dec.Recv != nil {
parseFunc.Method = parserParams(dec.Recv.List)
}
if dec.Type.Params != nil {
parseFunc.Params = parserParams(dec.Type.Params.List)
}
if dec.Type.Results != nil {
parseFunc.Result = parserParams(dec.Type.Results.List)
}
parse.Funcs = append(parse.Funcs, parseFunc)
}
}
parses = append(parses, parse)
}
return
}
func parserParams(fields []*ast.Field) (params []*param) {
for _, field := range fields {
p := &param{}
p.V = parseType(field.Type)
if field.Names == nil {
params = append(params, p)
}
for _, name := range field.Names {
sp := &param{}
sp.K = name.Name
sp.V = p.V
sp.P = p.P
params = append(params, sp)
}
}
return
}
func parseType(expr ast.Expr) string {
switch expr.(type) {
case *ast.Ident:
return expr.(*ast.Ident).Name
case *ast.StarExpr:
return "*" + parseType(expr.(*ast.StarExpr).X)
case *ast.ArrayType:
return "[" + parseType(expr.(*ast.ArrayType).Len) + "]" + parseType(expr.(*ast.ArrayType).Elt)
case *ast.SelectorExpr:
return parseType(expr.(*ast.SelectorExpr).X) + "." + expr.(*ast.SelectorExpr).Sel.Name
case *ast.MapType:
return "map[" + parseType(expr.(*ast.MapType).Key) + "]" + parseType(expr.(*ast.MapType).Value)
case *ast.StructType:
return "struct{}"
case *ast.InterfaceType:
return "interface{}"
case *ast.FuncType:
var (
pTemp string
rTemp string
)
pTemp = parseFuncType(pTemp, expr.(*ast.FuncType).Params)
if expr.(*ast.FuncType).Results != nil {
rTemp = parseFuncType(rTemp, expr.(*ast.FuncType).Results)
return fmt.Sprintf("func(%s) (%s)", pTemp, rTemp)
}
return fmt.Sprintf("func(%s)", pTemp)
case *ast.ChanType:
return fmt.Sprintf("make(chan %s)", parseType(expr.(*ast.ChanType).Value))
case *ast.Ellipsis:
return parseType(expr.(*ast.Ellipsis).Elt)
}
return ""
}
func parseFuncType(temp string, data *ast.FieldList) string {
var params = parserParams(data.List)
for i, param := range params {
if i == 0 {
temp = param.K + " " + param.V
continue
}
t := param.K + " " + param.V
temp = fmt.Sprintf("%s, %s", temp, t)
}
return temp
}
func parseImports(specs []ast.Spec) (params map[string]*param) {
params = make(map[string]*param)
for _, spec := range specs {
switch spec.(type) {
case *ast.ImportSpec:
p := &param{V: strings.Replace(spec.(*ast.ImportSpec).Path.Value, "\"", "", -1)}
if spec.(*ast.ImportSpec).Name != nil {
p.K = spec.(*ast.ImportSpec).Name.Name
params[p.K] = p
} else {
vs := strings.Split(p.V, "/")
params[vs[len(vs)-1]] = p
}
}
}
return
}

@ -0,0 +1,41 @@
package main
var (
tpPackage = "package %s\n\n"
tpImport = "import (\n\t%s\n)\n\n"
tpVar = "var (\n\t%s\n)\n"
tpInterface = "type %sInterface interface {\n%s}\n"
tpIntfcFunc = "%s(%s) %s\n"
tpMonkeyFunc = "// Mock%s .\nfunc Mock%s(%s %s,%s) (guard *monkey.PatchGuard) {\n\treturn monkey.PatchInstanceMethod(reflect.TypeOf(%s), \"%s\", func(_ %s, %s) (%s) {\n\t\treturn %s\n\t})\n}\n\n"
tpTestReset = "\n\t\tReset(func() {%s\n\t\t})"
tpTestFunc = "func Test%s%s(t *testing.T){%s\n\tConvey(\"%s\", t, func(){\n\t\t%s\tConvey(\"When everything goes positive\", func(){\n\t\t\t%s\n\t\t\t})\n\t\t})%s\n\t})\n}\n\n"
tpTestDaoMain = `func TestMain(m *testing.M) {
flag.Set("conf", "%s")
flag.Parse()
%s
os.Exit(m.Run())
}
`
tpTestServiceMain = `func TestMain(m *testing.M){
flag.Set("conf", "%s")
flag.Parse()
%s
os.Exit(m.Run())
}
`
tpTestMainNew = `if err := paladin.Init(); err != nil {
panic(err)
}
%s`
tpTestMainOld = `if err := conf.Init(); err != nil {
panic(err)
}
%s`
print = `Generation success!
莫生气
代码辣鸡非我意,
自己动手分田地;
你若气死谁如意?
谈笑风生活长命.
// Release 1.2.3. Powered by 主站质保团队`
)

@ -0,0 +1,42 @@
package main
import (
"fmt"
"strings"
"golang.org/x/tools/imports"
)
// GoImport Use golang.org/x/tools/imports auto import pkg
func GoImport(file string, bytes []byte) (res []byte, err error) {
options := &imports.Options{
TabWidth: 8,
TabIndent: true,
Comments: true,
Fragment: true,
}
if res, err = imports.Process(file, bytes, options); err != nil {
fmt.Printf("GoImport(%s) error(%v)", file, err)
res = bytes
return
}
return
}
// ConvertMethod checkout the file belongs to dao or not
func ConvertMethod(path string) (method string) {
switch {
case strings.Contains(path, "/dao"):
method = "d"
case strings.Contains(path, "/service"):
method = "s"
default:
method = ""
}
return
}
//ConvertHump convert words to hump style
func ConvertHump(words string) string {
return strings.ToUpper(words[0:1]) + words[1:]
}
Loading…
Cancel
Save