├── .travis.yml ├── license ├── mysqltest.go ├── mysqltest_test.go ├── patents └── readme.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - 1.3 6 | 7 | matrix: 8 | fast_finish: true 9 | 10 | before_install: 11 | - go get -v code.google.com/p/go.tools/cmd/vet 12 | - go get -v github.com/golang/lint/golint 13 | - go get -v code.google.com/p/go.tools/cmd/cover 14 | 15 | install: 16 | - go install -race -v std 17 | - go get -race -t -v ./... 18 | - go install -race -v ./... 19 | 20 | script: 21 | - go vet ./... 22 | - $HOME/gopath/bin/golint . 23 | - go test -cpu=2 -race -v ./... 24 | - go test -cpu=2 -covermode=atomic ./... 25 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For mysqltest software 4 | 5 | Copyright (c) 2014, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /mysqltest.go: -------------------------------------------------------------------------------- 1 | // Package mysqltest provides standalone test instances of mysql sutable for 2 | // use in tests. 3 | package mysqltest 4 | 5 | import ( 6 | "bytes" 7 | "database/sql" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "regexp" 15 | "text/template" 16 | "time" 17 | 18 | // We're optionally provide a DB instance backed by this driver. 19 | _ "github.com/go-sql-driver/mysql" 20 | 21 | "github.com/facebookgo/freeport" 22 | "github.com/facebookgo/waitout" 23 | ) 24 | 25 | var mysqlReadyForConnections = []byte("mysqld: ready for connections") 26 | 27 | var configTemplate, configTemplateErr = template.New("config").Parse(` 28 | [mysqld] 29 | bind-address = 127.0.0.1 30 | datadir = {{.DataDir}} 31 | innodb-buffer-pool-size = 5M 32 | innodb-buffer-pool-stats = 0 33 | innodb-log-file-size = 1M 34 | innodb-read-io-threads = 2 35 | innodb_additional_mem_pool_size = 1M 36 | innodb_mirrored_log_groups = 1 37 | key_buffer_size = 16K 38 | max-binlog-size = 256K 39 | max-delayed-threads = 5 40 | max_allowed_packet = 256K 41 | net_buffer_length = 2K 42 | port = {{.Port}} 43 | socket = {{.Socket}} 44 | sort_buffer_size = 32K 45 | sql_mode = '' 46 | thread_cache_size = 2 47 | thread_stack = 128K 48 | `) 49 | 50 | var mysqlBaseDir string 51 | 52 | func init() { 53 | if configTemplateErr != nil { 54 | panic(configTemplateErr) 55 | } 56 | 57 | out, err := exec.Command("mysqld", "--help", "--verbose").CombinedOutput() 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | // The spaces are important. 63 | hit := regexp.MustCompile(`basedir .*`).Find(out) 64 | mysqlBaseDir = string(bytes.TrimSpace(hit[8:])) 65 | } 66 | 67 | // Fatalf is satisfied by testing.T or testing.B. 68 | type Fatalf interface { 69 | Fatalf(format string, args ...interface{}) 70 | } 71 | 72 | // Server is a unique instance of a mysqld. 73 | type Server struct { 74 | Port int 75 | DataDir string 76 | Socket string 77 | T Fatalf 78 | cmd *exec.Cmd 79 | } 80 | 81 | // Start the server, this will return once the server has been started. 82 | func (s *Server) Start() { 83 | port, err := freeport.Get() 84 | if err != nil { 85 | s.T.Fatalf(err.Error()) 86 | } 87 | s.Port = port 88 | 89 | dir, err := ioutil.TempDir("", "mysql-DataDir-") 90 | if err != nil { 91 | s.T.Fatalf(err.Error()) 92 | } 93 | s.DataDir = dir 94 | s.Socket = filepath.Join(dir, "socket") 95 | 96 | cf, err := os.Create(filepath.Join(dir, "my.cnf")) 97 | if err != nil { 98 | s.T.Fatalf(err.Error()) 99 | } 100 | if err := configTemplate.Execute(cf, s); err != nil { 101 | s.T.Fatalf(err.Error()) 102 | } 103 | if err := cf.Close(); err != nil { 104 | s.T.Fatalf(err.Error()) 105 | } 106 | 107 | defaultsFile := fmt.Sprintf("--defaults-file=%s", cf.Name()) 108 | s.cmd = exec.Command("mysql_install_db", defaultsFile, "--basedir", mysqlBaseDir) 109 | if os.Getenv("MYSQLTEST_VERBOSE") == "1" { 110 | s.cmd.Stdout = os.Stdout 111 | s.cmd.Stderr = os.Stderr 112 | } 113 | if err := s.cmd.Run(); err != nil { 114 | s.T.Fatalf(err.Error()) 115 | } 116 | 117 | waiter := waitout.New(mysqlReadyForConnections) 118 | s.cmd = exec.Command("mysqld", defaultsFile, "--basedir", mysqlBaseDir) 119 | if os.Getenv("MYSQLTEST_VERBOSE") == "1" { 120 | s.cmd.Stdout = os.Stdout 121 | s.cmd.Stderr = io.MultiWriter(os.Stderr, waiter) 122 | } else { 123 | s.cmd.Stderr = waiter 124 | } 125 | if err := s.cmd.Start(); err != nil { 126 | s.T.Fatalf(err.Error()) 127 | } 128 | waiter.Wait() 129 | } 130 | 131 | // Stop the server, this will also remove all data. 132 | func (s *Server) Stop() { 133 | s.cmd.Process.Kill() 134 | os.RemoveAll(s.DataDir) 135 | } 136 | 137 | // DSN for the mysql server, suitable for use with sql.Open. The suffix is in 138 | // the form "dbname?param=value". 139 | func (s *Server) DSN(suffix string) string { 140 | return fmt.Sprintf("root@tcp(127.0.0.1:%d)/%s", s.Port, suffix) 141 | } 142 | 143 | // DB for the server. The suffix is in the form "dbname?param=value". 144 | func (s *Server) DB(suffix string) *sql.DB { 145 | db, err := sql.Open("mysql", s.DSN(suffix)) 146 | if err != nil { 147 | s.T.Fatalf(err.Error()) 148 | } 149 | return db 150 | } 151 | 152 | // NewStartedServer creates a new server starts it. 153 | func NewStartedServer(t Fatalf) *Server { 154 | for { 155 | s := &Server{T: t} 156 | start := make(chan struct{}) 157 | go func() { 158 | defer close(start) 159 | s.Start() 160 | }() 161 | select { 162 | case <-start: 163 | return s 164 | case <-time.After(30 * time.Second): 165 | } 166 | } 167 | } 168 | 169 | // NewServerDB creates a new server, starts it, creates the named DB, and 170 | // returns both. 171 | func NewServerDB(t Fatalf, db string) (*Server, *sql.DB) { 172 | s := NewStartedServer(t) 173 | if _, err := s.DB("").Exec("create database " + db); err != nil { 174 | t.Fatalf(err.Error()) 175 | } 176 | return s, s.DB(db) 177 | } 178 | -------------------------------------------------------------------------------- /mysqltest_test.go: -------------------------------------------------------------------------------- 1 | package mysqltest_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/facebookgo/mysqltest" 7 | ) 8 | 9 | func test(t *testing.T, answer int) { 10 | t.Parallel() 11 | mysql, db := mysqltest.NewServerDB(t, "metadb") 12 | defer mysql.Stop() 13 | 14 | const id = 1 15 | _, err := db.Exec(` 16 | create table metatable ( 17 | id int, 18 | answer int 19 | ) ENGINE = MEMORY; 20 | `) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | stmt, err := db.Prepare("insert into metatable values (?, ?)") 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | defer stmt.Close() 30 | 31 | if _, err := stmt.Exec(id, answer); err != nil { 32 | t.Fatal(err) 33 | } 34 | } 35 | 36 | // Testing that multiple instances don't stomp on each other. 37 | func TestOne(t *testing.T) { 38 | test(t, 42) 39 | } 40 | 41 | func TestTwo(t *testing.T) { 42 | test(t, 43) 43 | } 44 | 45 | func TestThree(t *testing.T) { 46 | test(t, 44) 47 | } 48 | -------------------------------------------------------------------------------- /patents: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights 2 | 3 | "Software" means the mysqltest software distributed by Facebook, Inc. 4 | 5 | Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive, 6 | irrevocable (subject to the termination provision below) license under any 7 | rights in any patent claims owned by Facebook, to make, have made, use, sell, 8 | offer to sell, import, and otherwise transfer the Software. For avoidance of 9 | doubt, no license is granted under Facebook’s rights in any patent claims that 10 | are infringed by (i) modifications to the Software made by you or a third party, 11 | or (ii) the Software in combination with any software or other technology 12 | provided by you or a third party. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | for anyone that makes any claim (including by filing any lawsuit, assertion or 16 | other action) alleging (a) direct, indirect, or contributory infringement or 17 | inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or 18 | affiliates, whether or not such claim is related to the Software, (ii) by any 19 | party if such claim arises in whole or in part from any software, product or 20 | service of Facebook or any of its subsidiaries or affiliates, whether or not 21 | such claim is related to the Software, or (iii) by any party relating to the 22 | Software; or (b) that any right in any patent claim of Facebook is invalid or 23 | unenforceable. 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | mysqltest [![Build Status](https://secure.travis-ci.org/facebookgo/mysqltest.png)](http://travis-ci.org/facebookgo/mysqltest) 2 | ========== 3 | 4 | Documentation: http://godoc.org/github.com/facebookgo/mysqltest 5 | --------------------------------------------------------------------------------