├── 2048 ├── .gitignore ├── .jshintrc ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── Rakefile ├── favicon.ico ├── index.html ├── js │ ├── animframe_polyfill.js │ ├── application.js │ ├── bind_polyfill.js │ ├── classlist_polyfill.js │ ├── game_manager.js │ ├── grid.js │ ├── html_actuator.js │ ├── keyboard_input_manager.js │ ├── local_storage_manager.js │ └── tile.js ├── meta │ ├── apple-touch-icon.png │ ├── apple-touch-startup-image-640x1096.png │ └── apple-touch-startup-image-640x920.png └── style │ ├── ai.css │ ├── fonts │ ├── ClearSans-Bold-webfont.eot │ ├── ClearSans-Bold-webfont.svg │ ├── ClearSans-Bold-webfont.woff │ ├── ClearSans-Light-webfont.eot │ ├── ClearSans-Light-webfont.svg │ ├── ClearSans-Light-webfont.woff │ ├── ClearSans-Regular-webfont.eot │ ├── ClearSans-Regular-webfont.svg │ ├── ClearSans-Regular-webfont.woff │ └── clear-sans.css │ ├── helpers.scss │ ├── main.css │ └── main.scss ├── .gitattributes ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── ai ├── ai.go └── ai_test.go ├── grid ├── grid.go └── grid_test.go ├── main.go ├── screenshot └── 2048-ai.jpg ├── utils ├── utils.go └── utils_test.go └── vendor ├── github.com ├── gorilla │ └── websocket │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── client.go │ │ ├── client_clone.go │ │ ├── client_clone_legacy.go │ │ ├── compression.go │ │ ├── conn.go │ │ ├── conn_read.go │ │ ├── conn_read_legacy.go │ │ ├── doc.go │ │ ├── json.go │ │ ├── mask.go │ │ ├── mask_safe.go │ │ ├── prepared.go │ │ ├── server.go │ │ └── util.go ├── jtolds │ └── gls │ │ ├── LICENSE │ │ ├── README.md │ │ ├── context.go │ │ ├── gen_sym.go │ │ ├── gid.go │ │ ├── id_pool.go │ │ ├── stack_tags.go │ │ ├── stack_tags_js.go │ │ └── stack_tags_main.go └── smartystreets │ ├── assertions │ ├── CONTRIBUTING.md │ ├── LICENSE.md │ ├── README.md │ ├── collections.go │ ├── doc.go │ ├── equal_method.go │ ├── equality.go │ ├── filter.go │ ├── internal │ │ ├── go-render │ │ │ ├── LICENSE │ │ │ └── render │ │ │ │ └── render.go │ │ └── oglematchers │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── any_of.go │ │ │ ├── contains.go │ │ │ ├── deep_equals.go │ │ │ ├── equals.go │ │ │ ├── greater_or_equal.go │ │ │ ├── greater_than.go │ │ │ ├── less_or_equal.go │ │ │ ├── less_than.go │ │ │ ├── matcher.go │ │ │ ├── not.go │ │ │ └── transform_description.go │ ├── messages.go │ ├── panic.go │ ├── quantity.go │ ├── serializer.go │ ├── strings.go │ ├── time.go │ └── type.go │ └── goconvey │ ├── LICENSE.md │ └── convey │ ├── assertions.go │ ├── context.go │ ├── convey.goconvey │ ├── discovery.go │ ├── doc.go │ ├── gotest │ └── utils.go │ ├── init.go │ ├── nilReporter.go │ └── reporting │ ├── console.go │ ├── doc.go │ ├── dot.go │ ├── gotest.go │ ├── init.go │ ├── json.go │ ├── printer.go │ ├── problems.go │ ├── reporter.go │ ├── reporting.goconvey │ ├── reports.go │ ├── statistics.go │ └── story.go └── vendor.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=go 2 | *.css linguist-language=go 3 | *.html linguist-language=go 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2048-ai 2 | 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 16 | .glide/ 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.8.x 4 | - 1.9.x 5 | -------------------------------------------------------------------------------- /2048/.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache/ 2 | -------------------------------------------------------------------------------- /2048/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true, 3 | "indent": 2, 4 | "maxlen": 80, 5 | "freeze": true, 6 | "camelcase": true, 7 | "unused": true, 8 | "eqnull": true, 9 | "proto": true, 10 | "supernew": true, 11 | "noyield": true, 12 | "evil": true, 13 | "node": true, 14 | "boss": true, 15 | "expr": true, 16 | "loopfunc": true, 17 | "white": true, 18 | "maxdepth": 4 19 | } 20 | -------------------------------------------------------------------------------- /2048/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Changes and improvements are more than welcome! Feel free to fork and open a pull request. 3 | 4 | Please follow the house rules to have a bigger chance of your contribution being merged. 5 | 6 | ## House rules 7 | 8 | ### How to make changes 9 | - To make changes, create a new branch based on `master` (do not create one from `gh-pages` unless strictly necessary) and make them there, then create a Pull Request to master. 10 | `gh-pages` is different from master in that it contains sharing features, analytics and other things that have no direct bearing with the game. `master` is the "pure" version of the game. 11 | - If you want to modify the CSS, please edit the SCSS files present in `style/`: `main.scss` and others. Don't edit the `main.css`, because it's supposed to be generated. 12 | In order to compile your SCSS modifications, you need to use the `sass` gem (install it by running `gem install sass` once Ruby is installed). 13 | To run SASS, simply use the following command: 14 | `sass --unix-newlines --watch style/main.scss` 15 | SASS will automatically recompile your css when changed. 16 | - `Rakefile` contains some tasks that help during development. Feel free to add useful tasks if needed. 17 | - Please use 2-space indentation when editing the JavaScript. A `.jshintrc` file is present, which will help your code to follow the guidelines if you install and run `jshint`. 18 | - Please test your modification thoroughly before submitting your Pull Request. 19 | 20 | ### Changes that might not be accepted 21 | We have to be conservative with the core game. This means that some modifications won't be merged, or will have to be evaluated carefully before being merged: 22 | 23 | - Undo/redo features 24 | - Save/reload features 25 | - Changes to how the tiles look or their contents 26 | - Changes to the layout 27 | - Changes to the grid size 28 | 29 | ### Changes that are welcome 30 | - Bug fixes 31 | - Compatibility improvements 32 | - "Under the hood" enhancements 33 | - Small changes that don't have an impact on the core gameplay 34 | -------------------------------------------------------------------------------- /2048/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Gabriele Cirulli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /2048/README.md: -------------------------------------------------------------------------------- 1 | # 2048 2 | A small clone of [1024](https://play.google.com/store/apps/details?id=com.veewo.a1024), based on [Saming's 2048](http://saming.fr/p/2048/) (also a clone). 3 | 4 | Made just for fun. [Play it here!](http://gabrielecirulli.github.io/2048/) 5 | 6 | The official app can also be found on the [Play Store](https://play.google.com/store/apps/details?id=com.gabrielecirulli.app2048) and [App Store!](https://itunes.apple.com/us/app/2048-by-gabriele-cirulli/id868076805) 7 | 8 | ### Contributions 9 | 10 | [Anna Harren](https://github.com/iirelu/) and [sigod](https://github.com/sigod) are maintainers for this repository. 11 | 12 | Other notable contributors: 13 | 14 | - [TimPetricola](https://github.com/TimPetricola) added best score storage 15 | - [chrisprice](https://github.com/chrisprice) added custom code for swipe handling on mobile 16 | - [marcingajda](https://github.com/marcingajda) made swipes work on Windows Phone 17 | - [mgarciaisaia](https://github.com/mgarciaisaia) added support for Android 2.3 18 | 19 | Many thanks to [rayhaanj](https://github.com/rayhaanj), [Mechazawa](https://github.com/Mechazawa), [grant](https://github.com/grant), [remram44](https://github.com/remram44) and [ghoullier](https://github.com/ghoullier) for the many other good contributions. 20 | 21 | ### Screenshot 22 | 23 |

24 | Screenshot 25 |

26 | 27 | That screenshot is fake, by the way. I never reached 2048 :smile: 28 | 29 | ## Contributing 30 | Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specific branch and request to pull into `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process. 31 | 32 | You can find the same information in the [contributing guide.](https://github.com/gabrielecirulli/2048/blob/master/CONTRIBUTING.md) 33 | 34 | ## License 35 | 2048 is licensed under the [MIT license.](https://github.com/gabrielecirulli/2048/blob/master/LICENSE.txt) 36 | 37 | ## Donations 38 | I made this in my spare time, and it's hosted on GitHub (which means I don't have any hosting costs), but if you enjoyed the game and feel like buying me coffee, you can donate at my BTC address: `1Ec6onfsQmoP9kkL3zkpB6c5sA4PVcXU2i`. Thank you very much! 39 | -------------------------------------------------------------------------------- /2048/Rakefile: -------------------------------------------------------------------------------- 1 | require "date" 2 | 3 | namespace :appcache do 4 | desc "update the date in the appcache file (in the gh-pages branch)" 5 | task :update do 6 | appcache = File.read("cache.appcache") 7 | updated = "# Updated: #{DateTime.now}" 8 | 9 | File.write("cache.appcache", appcache.sub(/^# Updated:.*$/, updated)) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /2048/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/favicon.ico -------------------------------------------------------------------------------- /2048/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2048 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |

2048

24 |
25 |
0
26 |
0
27 |
28 |
29 |
30 | AutoRun 31 | Hint 32 | NewGame 33 |
34 | 35 |
36 |
37 |

38 |
39 | Keep going 40 | Try again 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | 71 |
72 | 73 |
74 |
75 |
76 |
77 |

78 | Project source code 2048-ai. 79 |

80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /2048/js/animframe_polyfill.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var lastTime = 0; 3 | var vendors = ['webkit', 'moz']; 4 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 5 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 6 | window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || 7 | window[vendors[x] + 'CancelRequestAnimationFrame']; 8 | } 9 | 10 | if (!window.requestAnimationFrame) { 11 | window.requestAnimationFrame = function (callback) { 12 | var currTime = new Date().getTime(); 13 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 14 | var id = window.setTimeout(function () { 15 | callback(currTime + timeToCall); 16 | }, 17 | timeToCall); 18 | lastTime = currTime + timeToCall; 19 | return id; 20 | }; 21 | } 22 | 23 | if (!window.cancelAnimationFrame) { 24 | window.cancelAnimationFrame = function (id) { 25 | clearTimeout(id); 26 | }; 27 | } 28 | }()); 29 | -------------------------------------------------------------------------------- /2048/js/application.js: -------------------------------------------------------------------------------- 1 | // Wait till the browser is ready to render the game (avoids glitches) 2 | window.requestAnimationFrame(function () { 3 | new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager); 4 | }); 5 | -------------------------------------------------------------------------------- /2048/js/bind_polyfill.js: -------------------------------------------------------------------------------- 1 | Function.prototype.bind = Function.prototype.bind || function (target) { 2 | var self = this; 3 | return function (args) { 4 | if (!(args instanceof Array)) { 5 | args = [args]; 6 | } 7 | self.apply(target, args); 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /2048/js/classlist_polyfill.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | if (typeof window.Element === "undefined" || 3 | "classList" in document.documentElement) { 4 | return; 5 | } 6 | 7 | var prototype = Array.prototype, 8 | push = prototype.push, 9 | splice = prototype.splice, 10 | join = prototype.join; 11 | 12 | function DOMTokenList(el) { 13 | this.el = el; 14 | // The className needs to be trimmed and split on whitespace 15 | // to retrieve a list of classes. 16 | var classes = el.className.replace(/^\s+|\s+$/g, '').split(/\s+/); 17 | for (var i = 0; i < classes.length; i++) { 18 | push.call(this, classes[i]); 19 | } 20 | } 21 | 22 | DOMTokenList.prototype = { 23 | add: function (token) { 24 | if (this.contains(token)) return; 25 | push.call(this, token); 26 | this.el.className = this.toString(); 27 | }, 28 | contains: function (token) { 29 | return this.el.className.indexOf(token) != -1; 30 | }, 31 | item: function (index) { 32 | return this[index] || null; 33 | }, 34 | remove: function (token) { 35 | if (!this.contains(token)) return; 36 | for (var i = 0; i < this.length; i++) { 37 | if (this[i] == token) break; 38 | } 39 | splice.call(this, i, 1); 40 | this.el.className = this.toString(); 41 | }, 42 | toString: function () { 43 | return join.call(this, ' '); 44 | }, 45 | toggle: function (token) { 46 | if (!this.contains(token)) { 47 | this.add(token); 48 | } else { 49 | this.remove(token); 50 | } 51 | 52 | return this.contains(token); 53 | } 54 | }; 55 | 56 | window.DOMTokenList = DOMTokenList; 57 | 58 | function defineElementGetter(obj, prop, getter) { 59 | if (Object.defineProperty) { 60 | Object.defineProperty(obj, prop, { 61 | get: getter 62 | }); 63 | } else { 64 | obj.__defineGetter__(prop, getter); 65 | } 66 | } 67 | 68 | defineElementGetter(HTMLElement.prototype, 'classList', function () { 69 | return new DOMTokenList(this); 70 | }); 71 | })(); 72 | -------------------------------------------------------------------------------- /2048/js/grid.js: -------------------------------------------------------------------------------- 1 | function Grid(size, previousState) { 2 | this.size = size; 3 | this.cells = previousState ? this.fromState(previousState) : this.empty(); 4 | } 5 | 6 | // Build a grid of the specified size 7 | Grid.prototype.empty = function () { 8 | var cells = []; 9 | 10 | for (var x = 0; x < this.size; x++) { 11 | var row = cells[x] = []; 12 | 13 | for (var y = 0; y < this.size; y++) { 14 | row.push(null); 15 | } 16 | } 17 | 18 | return cells; 19 | }; 20 | 21 | Grid.prototype.fromState = function (state) { 22 | var cells = []; 23 | 24 | for (var x = 0; x < this.size; x++) { 25 | var row = cells[x] = []; 26 | 27 | for (var y = 0; y < this.size; y++) { 28 | var tile = state[x][y]; 29 | row.push(tile ? new Tile(tile.position, tile.value) : null); 30 | } 31 | } 32 | 33 | return cells; 34 | }; 35 | 36 | // Find the first available random position 37 | Grid.prototype.randomAvailableCell = function () { 38 | var cells = this.availableCells(); 39 | 40 | if (cells.length) { 41 | return cells[Math.floor(Math.random() * cells.length)]; 42 | } 43 | }; 44 | 45 | Grid.prototype.availableCells = function () { 46 | var cells = []; 47 | 48 | this.eachCell(function (x, y, tile) { 49 | if (!tile) { 50 | cells.push({ x: x, y: y }); 51 | } 52 | }); 53 | 54 | return cells; 55 | }; 56 | 57 | // Call callback for every cell 58 | Grid.prototype.eachCell = function (callback) { 59 | for (var x = 0; x < this.size; x++) { 60 | for (var y = 0; y < this.size; y++) { 61 | callback(x, y, this.cells[x][y]); 62 | } 63 | } 64 | }; 65 | 66 | // Check if there are any cells available 67 | Grid.prototype.cellsAvailable = function () { 68 | return !!this.availableCells().length; 69 | }; 70 | 71 | // Check if the specified cell is taken 72 | Grid.prototype.cellAvailable = function (cell) { 73 | return !this.cellOccupied(cell); 74 | }; 75 | 76 | Grid.prototype.cellOccupied = function (cell) { 77 | return !!this.cellContent(cell); 78 | }; 79 | 80 | Grid.prototype.cellContent = function (cell) { 81 | if (this.withinBounds(cell)) { 82 | return this.cells[cell.x][cell.y]; 83 | } else { 84 | return null; 85 | } 86 | }; 87 | 88 | // Inserts a tile at its position 89 | Grid.prototype.insertTile = function (tile) { 90 | this.cells[tile.x][tile.y] = tile; 91 | }; 92 | 93 | Grid.prototype.removeTile = function (tile) { 94 | this.cells[tile.x][tile.y] = null; 95 | }; 96 | 97 | Grid.prototype.withinBounds = function (position) { 98 | return position.x >= 0 && position.x < this.size && 99 | position.y >= 0 && position.y < this.size; 100 | }; 101 | 102 | Grid.prototype.serialize = function () { 103 | var cellState = []; 104 | 105 | for (var x = 0; x < this.size; x++) { 106 | var row = cellState[x] = []; 107 | 108 | for (var y = 0; y < this.size; y++) { 109 | row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null); 110 | } 111 | } 112 | 113 | return { 114 | size: this.size, 115 | cells: cellState 116 | }; 117 | }; 118 | 119 | Grid.prototype.toArray = function () { 120 | var data = new Array(); 121 | for(i=0;i<=3;i++){ 122 | data[i]=new Array(); 123 | } 124 | for(i=0;i<=3;i++){ 125 | var cells=this.cells[i]; 126 | for(j=0;j<=3;j++){ 127 | if(cells[j]){ 128 | data[j][i]=cells[j].value 129 | }else{ 130 | data[j][i]=0; 131 | } 132 | } 133 | } 134 | return data 135 | }; -------------------------------------------------------------------------------- /2048/js/html_actuator.js: -------------------------------------------------------------------------------- 1 | function HTMLActuator() { 2 | this.tileContainer = document.querySelector(".tile-container"); 3 | this.scoreContainer = document.querySelector(".score-container"); 4 | this.bestContainer = document.querySelector(".best-container"); 5 | this.messageContainer = document.querySelector(".game-message"); 6 | 7 | this.score = 0; 8 | } 9 | 10 | HTMLActuator.prototype.actuate = function (grid, metadata) { 11 | var self = this; 12 | 13 | window.requestAnimationFrame(function () { 14 | self.clearContainer(self.tileContainer); 15 | 16 | grid.cells.forEach(function (column) { 17 | column.forEach(function (cell) { 18 | if (cell) { 19 | self.addTile(cell); 20 | } 21 | }); 22 | }); 23 | 24 | self.updateScore(metadata.score); 25 | self.updateBestScore(metadata.bestScore); 26 | 27 | if (metadata.terminated) { 28 | if (metadata.over) { 29 | self.message(false); // You lose 30 | } else if (metadata.won) { 31 | self.message(true); // You win! 32 | } 33 | } 34 | 35 | }); 36 | }; 37 | 38 | // Continues the game (both restart and keep playing) 39 | HTMLActuator.prototype.continueGame = function () { 40 | this.clearMessage(); 41 | }; 42 | 43 | HTMLActuator.prototype.clearContainer = function (container) { 44 | while (container.firstChild) { 45 | container.removeChild(container.firstChild); 46 | } 47 | }; 48 | 49 | HTMLActuator.prototype.addTile = function (tile) { 50 | var self = this; 51 | 52 | var wrapper = document.createElement("div"); 53 | var inner = document.createElement("div"); 54 | var position = tile.previousPosition || { x: tile.x, y: tile.y }; 55 | var positionClass = this.positionClass(position); 56 | 57 | // We can't use classlist because it somehow glitches when replacing classes 58 | var classes = ["tile", "tile-" + tile.value, positionClass]; 59 | 60 | if (tile.value > 2048) classes.push("tile-super"); 61 | 62 | this.applyClasses(wrapper, classes); 63 | 64 | inner.classList.add("tile-inner"); 65 | inner.textContent = tile.value; 66 | 67 | if (tile.previousPosition) { 68 | // Make sure that the tile gets rendered in the previous position first 69 | window.requestAnimationFrame(function () { 70 | classes[2] = self.positionClass({ x: tile.x, y: tile.y }); 71 | self.applyClasses(wrapper, classes); // Update the position 72 | }); 73 | } else if (tile.mergedFrom) { 74 | classes.push("tile-merged"); 75 | this.applyClasses(wrapper, classes); 76 | 77 | // Render the tiles that merged 78 | tile.mergedFrom.forEach(function (merged) { 79 | self.addTile(merged); 80 | }); 81 | } else { 82 | classes.push("tile-new"); 83 | this.applyClasses(wrapper, classes); 84 | } 85 | 86 | // Add the inner part of the tile to the wrapper 87 | wrapper.appendChild(inner); 88 | 89 | // Put the tile on the board 90 | this.tileContainer.appendChild(wrapper); 91 | }; 92 | 93 | HTMLActuator.prototype.applyClasses = function (element, classes) { 94 | element.setAttribute("class", classes.join(" ")); 95 | }; 96 | 97 | HTMLActuator.prototype.normalizePosition = function (position) { 98 | return { x: position.x + 1, y: position.y + 1 }; 99 | }; 100 | 101 | HTMLActuator.prototype.positionClass = function (position) { 102 | position = this.normalizePosition(position); 103 | return "tile-position-" + position.x + "-" + position.y; 104 | }; 105 | 106 | HTMLActuator.prototype.updateScore = function (score) { 107 | this.clearContainer(this.scoreContainer); 108 | 109 | var difference = score - this.score; 110 | this.score = score; 111 | 112 | this.scoreContainer.textContent = this.score; 113 | 114 | if (difference > 0) { 115 | var addition = document.createElement("div"); 116 | addition.classList.add("score-addition"); 117 | addition.textContent = "+" + difference; 118 | 119 | this.scoreContainer.appendChild(addition); 120 | } 121 | }; 122 | 123 | HTMLActuator.prototype.updateBestScore = function (bestScore) { 124 | this.bestContainer.textContent = bestScore; 125 | }; 126 | 127 | HTMLActuator.prototype.message = function (won) { 128 | var type = won ? "game-won" : "game-over"; 129 | var message = won ? "You win!" : "Game over!"; 130 | 131 | this.messageContainer.classList.add(type); 132 | this.messageContainer.getElementsByTagName("p")[0].textContent = message; 133 | }; 134 | 135 | HTMLActuator.prototype.clearMessage = function () { 136 | // IE only takes one value to remove at a time. 137 | this.messageContainer.classList.remove("game-won"); 138 | this.messageContainer.classList.remove("game-over"); 139 | }; 140 | 141 | HTMLActuator.prototype.setHint = function(message) { 142 | document.getElementById('hint-button').innerHTML = message; 143 | }; 144 | 145 | HTMLActuator.prototype.setRunButton = function(message) { 146 | document.getElementById('ai-button').innerHTML = message; 147 | }; 148 | 149 | HTMLActuator.prototype.showHint = function(hint) { 150 | document.getElementById("hint-button").innerHTML = ['↑','→','↓','←'][hint]; 151 | } -------------------------------------------------------------------------------- /2048/js/keyboard_input_manager.js: -------------------------------------------------------------------------------- 1 | function KeyboardInputManager() { 2 | this.events = {}; 3 | 4 | if (window.navigator.msPointerEnabled) { 5 | //Internet Explorer 10 style 6 | this.eventTouchstart = "MSPointerDown"; 7 | this.eventTouchmove = "MSPointerMove"; 8 | this.eventTouchend = "MSPointerUp"; 9 | } else { 10 | this.eventTouchstart = "touchstart"; 11 | this.eventTouchmove = "touchmove"; 12 | this.eventTouchend = "touchend"; 13 | } 14 | 15 | this.listen(); 16 | } 17 | 18 | KeyboardInputManager.prototype.on = function (event, callback) { 19 | if (!this.events[event]) { 20 | this.events[event] = []; 21 | } 22 | this.events[event].push(callback); 23 | }; 24 | 25 | KeyboardInputManager.prototype.emit = function (event, data) { 26 | var callbacks = this.events[event]; 27 | if (callbacks) { 28 | callbacks.forEach(function (callback) { 29 | callback(data); 30 | }); 31 | } 32 | }; 33 | 34 | KeyboardInputManager.prototype.listen = function () { 35 | var self = this; 36 | 37 | var map = { 38 | 38: 0, // Up 39 | 39: 1, // Right 40 | 40: 2, // Down 41 | 37: 3, // Left 42 | 75: 0, // Vim up 43 | 76: 1, // Vim right 44 | 74: 2, // Vim down 45 | 72: 3, // Vim left 46 | 87: 0, // W 47 | 68: 1, // D 48 | 83: 2, // S 49 | 65: 3 // A 50 | }; 51 | 52 | // Respond to direction keys 53 | document.addEventListener("keydown", function (event) { 54 | var modifiers = event.altKey || event.ctrlKey || event.metaKey || 55 | event.shiftKey; 56 | var mapped = map[event.which]; 57 | 58 | if (!modifiers) { 59 | if (mapped !== undefined) { 60 | event.preventDefault(); 61 | self.emit("move", mapped); 62 | } 63 | } 64 | 65 | // R key restarts the game 66 | if (!modifiers && event.which === 82) { 67 | self.restart.call(self, event); 68 | } 69 | }); 70 | 71 | // Respond to button presses 72 | this.bindButtonPress(".retry-button", this.restart); 73 | this.bindButtonPress(".restart-button", this.restart); 74 | this.bindButtonPress(".hint-button", this.hint); 75 | this.bindButtonPress(".ai-button", this.autoRun); 76 | this.bindButtonPress(".keep-playing-button", this.keepPlaying); 77 | 78 | // Respond to swipe events 79 | var touchStartClientX, touchStartClientY; 80 | var gameContainer = document.getElementsByClassName("game-container")[0]; 81 | 82 | gameContainer.addEventListener(this.eventTouchstart, function (event) { 83 | if ((!window.navigator.msPointerEnabled && event.touches.length > 1) || 84 | event.targetTouches.length > 1) { 85 | return; // Ignore if touching with more than 1 finger 86 | } 87 | 88 | if (window.navigator.msPointerEnabled) { 89 | touchStartClientX = event.pageX; 90 | touchStartClientY = event.pageY; 91 | } else { 92 | touchStartClientX = event.touches[0].clientX; 93 | touchStartClientY = event.touches[0].clientY; 94 | } 95 | 96 | event.preventDefault(); 97 | }); 98 | 99 | gameContainer.addEventListener(this.eventTouchmove, function (event) { 100 | event.preventDefault(); 101 | }); 102 | 103 | gameContainer.addEventListener(this.eventTouchend, function (event) { 104 | if ((!window.navigator.msPointerEnabled && event.touches.length > 0) || 105 | event.targetTouches.length > 0) { 106 | return; // Ignore if still touching with one or more fingers 107 | } 108 | 109 | var touchEndClientX, touchEndClientY; 110 | 111 | if (window.navigator.msPointerEnabled) { 112 | touchEndClientX = event.pageX; 113 | touchEndClientY = event.pageY; 114 | } else { 115 | touchEndClientX = event.changedTouches[0].clientX; 116 | touchEndClientY = event.changedTouches[0].clientY; 117 | } 118 | 119 | var dx = touchEndClientX - touchStartClientX; 120 | var absDx = Math.abs(dx); 121 | 122 | var dy = touchEndClientY - touchStartClientY; 123 | var absDy = Math.abs(dy); 124 | 125 | if (Math.max(absDx, absDy) > 10) { 126 | // (right : left) : (down : up) 127 | self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0)); 128 | } 129 | }); 130 | }; 131 | 132 | KeyboardInputManager.prototype.hint = function (event) { 133 | this.emit("hint"); 134 | }; 135 | 136 | KeyboardInputManager.prototype.autoRun = function (event) { 137 | this.emit("autoRun"); 138 | }; 139 | 140 | KeyboardInputManager.prototype.restart = function (event) { 141 | event.preventDefault(); 142 | this.emit("restart"); 143 | }; 144 | 145 | KeyboardInputManager.prototype.keepPlaying = function (event) { 146 | event.preventDefault(); 147 | this.emit("keepPlaying"); 148 | }; 149 | 150 | KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) { 151 | var button = document.querySelector(selector); 152 | button.addEventListener("click", fn.bind(this)); 153 | button.addEventListener(this.eventTouchend, fn.bind(this)); 154 | }; -------------------------------------------------------------------------------- /2048/js/local_storage_manager.js: -------------------------------------------------------------------------------- 1 | window.fakeStorage = { 2 | _data: {}, 3 | 4 | setItem: function (id, val) { 5 | return this._data[id] = String(val); 6 | }, 7 | 8 | getItem: function (id) { 9 | return this._data.hasOwnProperty(id) ? this._data[id] : undefined; 10 | }, 11 | 12 | removeItem: function (id) { 13 | return delete this._data[id]; 14 | }, 15 | 16 | clear: function () { 17 | return this._data = {}; 18 | } 19 | }; 20 | 21 | function LocalStorageManager() { 22 | this.bestScoreKey = "bestScore"; 23 | this.gameStateKey = "gameState"; 24 | 25 | var supported = this.localStorageSupported(); 26 | this.storage = supported ? window.localStorage : window.fakeStorage; 27 | } 28 | 29 | LocalStorageManager.prototype.localStorageSupported = function () { 30 | var testKey = "test"; 31 | 32 | try { 33 | var storage = window.localStorage; 34 | storage.setItem(testKey, "1"); 35 | storage.removeItem(testKey); 36 | return true; 37 | } catch (error) { 38 | return false; 39 | } 40 | }; 41 | 42 | // Best score getters/setters 43 | LocalStorageManager.prototype.getBestScore = function () { 44 | return this.storage.getItem(this.bestScoreKey) || 0; 45 | }; 46 | 47 | LocalStorageManager.prototype.setBestScore = function (score) { 48 | this.storage.setItem(this.bestScoreKey, score); 49 | }; 50 | 51 | // Game state getters/setters and clearing 52 | LocalStorageManager.prototype.getGameState = function () { 53 | var stateJSON = this.storage.getItem(this.gameStateKey); 54 | return stateJSON ? JSON.parse(stateJSON) : null; 55 | }; 56 | 57 | LocalStorageManager.prototype.setGameState = function (gameState) { 58 | this.storage.setItem(this.gameStateKey, JSON.stringify(gameState)); 59 | }; 60 | 61 | LocalStorageManager.prototype.clearGameState = function () { 62 | this.storage.removeItem(this.gameStateKey); 63 | }; 64 | -------------------------------------------------------------------------------- /2048/js/tile.js: -------------------------------------------------------------------------------- 1 | function Tile(position, value) { 2 | this.x = position.x; 3 | this.y = position.y; 4 | this.value = value || 2; 5 | 6 | this.previousPosition = null; 7 | this.mergedFrom = null; // Tracks tiles that merged together 8 | } 9 | 10 | Tile.prototype.savePosition = function () { 11 | this.previousPosition = { x: this.x, y: this.y }; 12 | }; 13 | 14 | Tile.prototype.updatePosition = function (position) { 15 | this.x = position.x; 16 | this.y = position.y; 17 | }; 18 | 19 | Tile.prototype.serialize = function () { 20 | return { 21 | position: { 22 | x: this.x, 23 | y: this.y 24 | }, 25 | value: this.value 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /2048/meta/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/meta/apple-touch-icon.png -------------------------------------------------------------------------------- /2048/meta/apple-touch-startup-image-640x1096.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/meta/apple-touch-startup-image-640x1096.png -------------------------------------------------------------------------------- /2048/meta/apple-touch-startup-image-640x920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/meta/apple-touch-startup-image-640x920.png -------------------------------------------------------------------------------- /2048/style/ai.css: -------------------------------------------------------------------------------- 1 | .ai-button { 2 | display: inline-block; 3 | background: #8f7a66; 4 | border-radius: 3px; 5 | padding: 0 20px; 6 | text-decoration: none; 7 | color: #f9f6f2; 8 | height: 40px; 9 | line-height: 42px; 10 | display: block; 11 | text-align: center; 12 | float: left; 13 | width: 20%; 14 | padding: 0; 15 | box-sizing: border-box; 16 | margin-top: 2px; 17 | } 18 | 19 | .hint-button { 20 | display: inline-block; 21 | background: #8f7a66; 22 | border-radius: 3px; 23 | padding: 0 20px; 24 | text-decoration: none; 25 | color: #f9f6f2; 26 | height: 40px; 27 | line-height: 42px; 28 | text-align: center; 29 | float: none; 30 | width: 20%; 31 | padding: 0; 32 | box-sizing: border-box; 33 | margin-top: 2px; 34 | } -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Bold-webfont.eot -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Bold-webfont.woff -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Light-webfont.eot -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Light-webfont.woff -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Regular-webfont.eot -------------------------------------------------------------------------------- /2048/style/fonts/ClearSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/2048/style/fonts/ClearSans-Regular-webfont.woff -------------------------------------------------------------------------------- /2048/style/fonts/clear-sans.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Clear Sans"; 3 | src: url("ClearSans-Light-webfont.eot"); 4 | src: url("ClearSans-Light-webfont.eot?#iefix") format("embedded-opentype"), 5 | url("ClearSans-Light-webfont.svg#clear_sans_lightregular") format("svg"), 6 | url("ClearSans-Light-webfont.woff") format("woff"); 7 | font-weight: 200; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: "Clear Sans"; 13 | src: url("ClearSans-Regular-webfont.eot"); 14 | src: url("ClearSans-Regular-webfont.eot?#iefix") format("embedded-opentype"), 15 | url("ClearSans-Regular-webfont.svg#clear_sansregular") format("svg"), 16 | url("ClearSans-Regular-webfont.woff") format("woff"); 17 | font-weight: normal; 18 | font-style: normal; 19 | } 20 | 21 | @font-face { 22 | font-family: "Clear Sans"; 23 | src: url("ClearSans-Bold-webfont.eot"); 24 | src: url("ClearSans-Bold-webfont.eot?#iefix") format("embedded-opentype"), 25 | url("ClearSans-Bold-webfont.svg#clear_sansbold") format("svg"), 26 | url("ClearSans-Bold-webfont.woff") format("woff"); 27 | font-weight: 700; 28 | font-style: normal; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /2048/style/helpers.scss: -------------------------------------------------------------------------------- 1 | // Exponent 2 | // From: https://github.com/Team-Sass/Sassy-math/blob/master/sass/math.scss#L36 3 | 4 | @function exponent($base, $exponent) { 5 | // reset value 6 | $value: $base; 7 | // positive intergers get multiplied 8 | @if $exponent > 1 { 9 | @for $i from 2 through $exponent { 10 | $value: $value * $base; } } 11 | // negitive intergers get divided. A number divided by itself is 1 12 | @if $exponent < 1 { 13 | @for $i from 0 through -$exponent { 14 | $value: $value / $base; } } 15 | // return the last value written 16 | @return $value; 17 | } 18 | 19 | @function pow($base, $exponent) { 20 | @return exponent($base, $exponent); 21 | } 22 | 23 | // Transition mixins 24 | @mixin transition($args...) { 25 | -webkit-transition: $args; 26 | -moz-transition: $args; 27 | transition: $args; 28 | } 29 | 30 | @mixin transition-property($args...) { 31 | -webkit-transition-property: $args; 32 | -moz-transition-property: $args; 33 | transition-property: $args; 34 | } 35 | 36 | @mixin animation($args...) { 37 | -webkit-animation: $args; 38 | -moz-animation: $args; 39 | animation: $args; 40 | } 41 | 42 | @mixin animation-fill-mode($args...) { 43 | -webkit-animation-fill-mode: $args; 44 | -moz-animation-fill-mode: $args; 45 | animation-fill-mode: $args; 46 | } 47 | 48 | @mixin transform($args...) { 49 | -webkit-transform: $args; 50 | -moz-transform: $args; 51 | -ms-transform: $args; 52 | transform: $args; 53 | } 54 | 55 | // Keyframe animations 56 | @mixin keyframes($animation-name) { 57 | @-webkit-keyframes $animation-name { 58 | @content; 59 | } 60 | @-moz-keyframes $animation-name { 61 | @content; 62 | } 63 | @keyframes $animation-name { 64 | @content; 65 | } 66 | } 67 | 68 | // Media queries 69 | @mixin smaller($width) { 70 | @media screen and (max-width: $width) { 71 | @content; 72 | } 73 | } 74 | 75 | // Clearfix 76 | @mixin clearfix { 77 | &:after { 78 | content: ""; 79 | display: block; 80 | clear: both; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9 as builder 2 | WORKDIR /go/src/github.com/xwjdsh/2048-ai 3 | COPY . . 4 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o 2048-ai . 5 | 6 | FROM alpine:latest 7 | LABEL maintainer="iwendellsun@gmail.com" 8 | RUN apk --no-cache add ca-certificates tzdata \ 9 | && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 10 | && echo "Asia/Shanghai" > /etc/timezone \ 11 | && apk del tzdata 12 | WORKDIR /root/2048-ai 13 | COPY --from=builder /go/src/github.com/xwjdsh/2048-ai/2048-ai . 14 | COPY --from=builder /go/src/github.com/xwjdsh/2048-ai/2048 ./2048 15 | EXPOSE 8080 16 | CMD ["./2048-ai"] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Wendell Sun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2048-AI 2 | 3 | [![Build Status](https://travis-ci.org/xwjdsh/2048-ai.svg?branch=master)](https://travis-ci.org/xwjdsh/2048-ai) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/xwjdsh/2048-ai)](https://goreportcard.com/report/github.com/xwjdsh/2048-ai) 5 | [![](https://images.microbadger.com/badges/image/wendellsun/2048-ai.svg)](https://microbadger.com/images/wendellsun/2048-ai "Get your own image badge on microbadger.com") 6 | [![DUB](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/xwjdsh/2048-ai/blob/master/LICENSE) 7 | 8 | AI for the 2048 game, implements by expectimax search, powered by Go. 9 | 10 | The web front-ends of 2048 game was forked from [gabrielecirulli/2048](https://github.com/gabrielecirulli/2048), respect and gratitude! 11 | 12 | ## Screenshot 13 | ![](https://raw.githubusercontent.com/xwjdsh/2048-ai/master/screenshot/2048-ai.jpg) 14 | 15 | ## How to run? 16 | #### Via Go 17 | ```bash 18 | go get github.com/xwjdsh/2048-ai 19 | cd $GOPATH/src/github.com/xwjdsh/2048-ai 20 | go build 21 | ./2048-ai 22 | ``` 23 | 24 | #### Via Docker 25 | ```shell 26 | docker run -p 8080:8080 wendellsun/2048-ai 27 | ``` 28 | 29 | or build images by yourself: 30 | 31 | ```shell 32 | # clone and enter project 33 | docker build -t 2048-ai . 34 | docker run -p 8080:8080 2048-ai 35 | ``` 36 | 37 | Then, you can access http://localhost:8080/ from the browser. 38 | 39 | ## Related 40 | 41 | * [Expectimax search ](https://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&cad=rja&uact=8&ved=0ahUKEwiVrsfmiojXAhWExbwKHa6GAuYQFgg3MAI&url=https%3A%2F%2Fweb.uvic.ca%2F~maryam%2FAISpring94%2FSlides%2F06_ExpectimaxSearch.pdf&usg=AOvVaw0pjG10MxUtkBvM-mvRNlew) 42 | * [Model weight matrix](https://codemyroad.wordpress.com/2014/05/14/2048-ai-the-intelligent-bot/) 43 | * Communication by websocket, implements by [gorilla/websocket](https://github.com/gorilla/websocket) 44 | 45 | ## Licence 46 | 47 | [MIT License](https://github.com/xwjdsh/2048-ai/blob/master/LICENSE) 48 | -------------------------------------------------------------------------------- /ai/ai.go: -------------------------------------------------------------------------------- 1 | package ai 2 | 3 | import ( 4 | "github.com/xwjdsh/2048-ai/grid" 5 | ) 6 | 7 | type AI struct { 8 | // Grid is 4x4 grid. 9 | Grid *grid.Grid 10 | // Active is true represent need to select a direction to move, else represent computer need fill a number("2" or "4") into grid. 11 | Active bool 12 | } 13 | 14 | var directions = []grid.Direction{ 15 | grid.UP, 16 | grid.LEFT, 17 | grid.DOWN, 18 | grid.RIGHT, 19 | } 20 | 21 | // The chance is 10% about fill "4" into grid and 90% fill "2" in the 2048 game. 22 | var expectMap = map[int]float64{ 23 | 2: 0.9, 24 | 4: 0.1, 25 | } 26 | 27 | var ( 28 | // There are three model weight matrix, represents three formation for 2048 game, it from internet. 29 | // The evaluate function is simple and crude, so actually it's not stable. 30 | // If you feel interesting in evaluation function, you can read https://github.com/ovolve/2048-AI project source code. 31 | model1 = [][]int{ 32 | {16, 15, 14, 13}, 33 | {9, 10, 11, 12}, 34 | {8, 7, 6, 5}, 35 | {1, 2, 3, 4}, 36 | } 37 | model2 = [][]int{ 38 | {16, 15, 12, 4}, 39 | {14, 13, 11, 3}, 40 | {10, 9, 8, 2}, 41 | {7, 6, 5, 1}, 42 | } 43 | model3 = [][]int{ 44 | {16, 15, 14, 4}, 45 | {13, 12, 11, 3}, 46 | {10, 9, 8, 2}, 47 | {7, 6, 5, 1}, 48 | } 49 | ) 50 | 51 | // Search method compute each could move direction score result by expect search algorithm 52 | func (a *AI) Search() grid.Direction { 53 | var ( 54 | bestDire = grid.NONE 55 | bestScore float64 = -1 56 | ) 57 | // depth value depending on grid's max value. 58 | dept := a.deptSelect() 59 | for _, dire := range directions { 60 | newGrid := a.Grid.Clone() 61 | if newGrid.Move(dire) { 62 | // Could move. 63 | // Active is false represent computer should fill number to grid now. 64 | newAI := &AI{Grid: newGrid, Active: false} 65 | if newScore := newAI.expectSearch(dept); newScore > bestScore { 66 | bestDire = dire 67 | bestScore = newScore 68 | } 69 | } 70 | } 71 | return bestDire 72 | } 73 | 74 | // expect search implements 75 | func (a *AI) expectSearch(dept int) float64 { 76 | if dept == 0 { 77 | return float64(a.score()) 78 | } 79 | var score float64 80 | if a.Active { 81 | for _, d := range directions { 82 | newGrid := a.Grid.Clone() 83 | if newGrid.Move(d) { 84 | newAI := &AI{Grid: newGrid, Active: false} 85 | if newScore := newAI.expectSearch(dept - 1); newScore > score { 86 | score = newScore 87 | } 88 | } 89 | } 90 | } else { 91 | // computer fill a number to grid now, it will try each vacant point with "2" or "4" 92 | points := a.Grid.VacantPoints() 93 | for k, v := range expectMap { 94 | for _, point := range points { 95 | newGrid := a.Grid.Clone() 96 | newGrid.Data[point.X][point.Y] = k 97 | // Change active, select a direction to move now. 98 | newAI := &AI{Grid: newGrid, Active: true} 99 | newScore := newAI.expectSearch(dept - 1) 100 | score += float64(newScore) * v 101 | } 102 | } 103 | score /= float64(len(points)) 104 | } 105 | return score 106 | } 107 | 108 | // score method evaluate a grid 109 | func (a *AI) score() int { 110 | result := make([]int, 24) 111 | for x := 0; x < 4; x++ { 112 | for y := 0; y < 4; y++ { 113 | if value := a.Grid.Data[x][y]; value != 0 { 114 | // get eight result(rotate and flip grid) for each model, 115 | modelScore(0, x, y, value, model1, &result) 116 | modelScore(1, x, y, value, model2, &result) 117 | modelScore(2, x, y, value, model3, &result) 118 | } 119 | } 120 | } 121 | // get max score in above 24 result, apply best formation 122 | var max int 123 | for _, v := range result { 124 | if v > max { 125 | max = v 126 | } 127 | } 128 | return max 129 | } 130 | 131 | // get eight result(rotate and flip grid) for each model 132 | func modelScore(index, x, y, value int, model [][]int, result *[]int) { 133 | start := index * 8 134 | r := *result 135 | r[start] += value * model[x][y] 136 | r[start+1] += value * model[x][3-y] 137 | 138 | r[start+2] += value * model[y][x] 139 | r[start+3] += value * model[3-y][x] 140 | 141 | r[start+4] += value * model[3-x][3-y] 142 | r[start+5] += value * model[3-x][y] 143 | 144 | r[start+6] += value * model[y][3-x] 145 | r[start+7] += value * model[3-y][3-x] 146 | } 147 | 148 | // the return value is search depth, it depending on grid's max value 149 | // the max value larger and depth larger, this will takes more calculations and make move became slowly but maybe have a better score result. 150 | func (a *AI) deptSelect() int { 151 | dept := 4 152 | max := a.Grid.Max() 153 | if max >= 2048 { 154 | dept = 6 155 | } else if max >= 1024 { 156 | dept = 5 157 | } 158 | return dept 159 | } 160 | -------------------------------------------------------------------------------- /ai/ai_test.go: -------------------------------------------------------------------------------- 1 | package ai 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xwjdsh/2048-ai/grid" 7 | ) 8 | 9 | func TestSearch(t *testing.T) { 10 | g := &grid.Grid{ 11 | Data: [4][4]int{ 12 | {2, 128, 32, 4}, 13 | {4, 512, 128, 4}, 14 | {2048, 32, 2, 16}, 15 | {4096, 4, 16, 4}, 16 | }, 17 | } 18 | a := &AI{Grid: g} 19 | dire := a.Search() 20 | if dire != grid.UP && dire != grid.DOWN { 21 | t.Errorf("direction error, should be UP or DOWN, but got %v", dire) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /grid/grid.go: -------------------------------------------------------------------------------- 1 | package grid 2 | 3 | import "github.com/xwjdsh/2048-ai/utils" 4 | 5 | type Direction int 6 | 7 | const ( 8 | UP Direction = iota 9 | RIGHT 10 | DOWN 11 | LEFT 12 | 13 | NONE 14 | ) 15 | 16 | type Grid struct { 17 | Data [4][4]int `json:"data"` 18 | } 19 | 20 | func (g *Grid) Clone() *Grid { 21 | gridClone := &Grid{} 22 | *gridClone = *g 23 | return gridClone 24 | } 25 | 26 | func (g *Grid) Max() int { 27 | max := 0 28 | for _, row := range g.Data { 29 | for _, value := range row { 30 | if value > max { 31 | max = value 32 | } 33 | } 34 | } 35 | return max 36 | } 37 | 38 | func (g *Grid) VacantPoints() []utils.Point { 39 | points := []utils.Point{} 40 | for x, row := range g.Data { 41 | for y, value := range row { 42 | if value == 0 { 43 | points = append(points, utils.Point{X: x, Y: y}) 44 | } 45 | } 46 | } 47 | return points 48 | } 49 | 50 | func (g *Grid) Move(d Direction) bool { 51 | originData := g.Data 52 | data := &g.Data 53 | switch d { 54 | case UP: 55 | for y := 0; y < 4; y++ { 56 | for x := 0; x < 3; x++ { 57 | for nx := x + 1; nx <= 3; nx++ { 58 | if data[nx][y] > 0 { 59 | if data[x][y] <= 0 { 60 | data[x][y] = data[nx][y] 61 | data[nx][y] = 0 62 | x -= 1 63 | } else if data[x][y] == data[nx][y] { 64 | data[x][y] += data[nx][y] 65 | data[nx][y] = 0 66 | } 67 | break 68 | } 69 | } 70 | } 71 | } 72 | case DOWN: 73 | for y := 0; y < 4; y++ { 74 | for x := 3; x > 0; x-- { 75 | for nx := x - 1; nx >= 0; nx-- { 76 | if data[nx][y] > 0 { 77 | if data[x][y] <= 0 { 78 | data[x][y] = data[nx][y] 79 | data[nx][y] = 0 80 | x += 1 81 | } else if data[x][y] == data[nx][y] { 82 | data[x][y] += data[nx][y] 83 | data[nx][y] = 0 84 | } 85 | break 86 | } 87 | } 88 | } 89 | } 90 | case LEFT: 91 | for x := 0; x < 4; x++ { 92 | for y := 0; y < 3; y++ { 93 | for ny := y + 1; ny <= 3; ny++ { 94 | if data[x][ny] > 0 { 95 | if data[x][y] <= 0 { 96 | data[x][y] = data[x][ny] 97 | data[x][ny] = 0 98 | y -= 1 99 | } else if data[x][y] == data[x][ny] { 100 | data[x][y] += data[x][ny] 101 | data[x][ny] = 0 102 | } 103 | break 104 | } 105 | } 106 | } 107 | } 108 | case RIGHT: 109 | for x := 0; x < 4; x++ { 110 | for y := 3; y > 0; y-- { 111 | for ny := y - 1; ny >= 0; ny-- { 112 | if data[x][ny] > 0 { 113 | if data[x][y] <= 0 { 114 | data[x][y] = data[x][ny] 115 | data[x][ny] = 0 116 | y += 1 117 | } else if data[x][y] == data[x][ny] { 118 | data[x][y] += data[x][ny] 119 | data[x][ny] = 0 120 | } 121 | break 122 | } 123 | } 124 | } 125 | } 126 | } 127 | return utils.Diff(*data, originData) 128 | } 129 | -------------------------------------------------------------------------------- /grid/grid_test.go: -------------------------------------------------------------------------------- 1 | package grid 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestMove(t *testing.T) { 10 | Convey("Init data", t, func() { 11 | Convey("Init grid data1", func() { 12 | g := &Grid{Data: [4][4]int{ 13 | {2, 2, 0, 4}, 14 | {2, 2, 4, 0}, 15 | {2, 2, 2, 2}, 16 | {2, 2, 4, 4}, 17 | }} 18 | Convey("Move Up", func() { 19 | gu := &Grid{} 20 | *gu = *g 21 | result := gu.Move(UP) 22 | So(result, ShouldEqual, true) 23 | So(gu.Data, ShouldEqual, [4][4]int{ 24 | {4, 4, 4, 4}, 25 | {4, 4, 2, 2}, 26 | {0, 0, 4, 4}, 27 | {0, 0, 0, 0}, 28 | }) 29 | }) 30 | Convey("Move Down", func() { 31 | gu := &Grid{} 32 | *gu = *g 33 | result := gu.Move(DOWN) 34 | So(result, ShouldEqual, true) 35 | So(gu.Data, ShouldEqual, [4][4]int{ 36 | {0, 0, 0, 0}, 37 | {0, 0, 4, 4}, 38 | {4, 4, 2, 2}, 39 | {4, 4, 4, 4}, 40 | }) 41 | }) 42 | Convey("Move Left", func() { 43 | gu := &Grid{} 44 | *gu = *g 45 | result := gu.Move(LEFT) 46 | So(result, ShouldEqual, true) 47 | So(gu.Data, ShouldEqual, [4][4]int{ 48 | {4, 4, 0, 0}, 49 | {4, 4, 0, 0}, 50 | {4, 4, 0, 0}, 51 | {4, 8, 0, 0}, 52 | }) 53 | }) 54 | Convey("Move Right", func() { 55 | gu := &Grid{} 56 | *gu = *g 57 | result := gu.Move(RIGHT) 58 | So(result, ShouldEqual, true) 59 | So(gu.Data, ShouldEqual, [4][4]int{ 60 | {0, 0, 4, 4}, 61 | {0, 0, 4, 4}, 62 | {0, 0, 4, 4}, 63 | {0, 0, 4, 8}, 64 | }) 65 | }) 66 | }) 67 | Convey("Init grid data2", func() { 68 | g := &Grid{Data: [4][4]int{ 69 | {2, 4, 2, 4}, 70 | {4, 8, 4, 8}, 71 | {2, 4, 2, 4}, 72 | {4, 8, 4, 8}, 73 | }} 74 | Convey("Move Up", func() { 75 | gu := &Grid{} 76 | *gu = *g 77 | result := gu.Move(UP) 78 | So(result, ShouldEqual, false) 79 | }) 80 | Convey("Move Down", func() { 81 | gu := &Grid{} 82 | *gu = *g 83 | result := gu.Move(DOWN) 84 | So(result, ShouldEqual, false) 85 | }) 86 | Convey("Move Left", func() { 87 | gu := &Grid{} 88 | *gu = *g 89 | result := gu.Move(LEFT) 90 | So(result, ShouldEqual, false) 91 | }) 92 | Convey("Move Right", func() { 93 | gu := &Grid{} 94 | *gu = *g 95 | result := gu.Move(RIGHT) 96 | So(result, ShouldEqual, false) 97 | }) 98 | }) 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "html/template" 8 | "log" 9 | "net/http" 10 | "sync/atomic" 11 | 12 | "github.com/gorilla/websocket" 13 | "github.com/xwjdsh/2048-ai/ai" 14 | "github.com/xwjdsh/2048-ai/grid" 15 | ) 16 | 17 | var upgrader = websocket.Upgrader{ 18 | ReadBufferSize: 1024, 19 | WriteBufferSize: 1024, 20 | CheckOrigin: func(r *http.Request) bool { 21 | return true 22 | }, 23 | } 24 | 25 | var logo = ` 26 | ██████╗ ██████╗ ██╗ ██╗ █████╗ █████╗ ██╗ 27 | ╚════██╗██╔═████╗██║ ██║██╔══██╗ ██╔══██╗██║ 28 | █████╔╝██║██╔██║███████║╚█████╔╝█████╗███████║██║ 29 | ██╔═══╝ ████╔╝██║╚════██║██╔══██╗╚════╝██╔══██║██║ 30 | ███████╗╚██████╔╝ ██║╚█████╔╝ ██║ ██║██║ 31 | ╚══════╝ ╚═════╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝╚═╝ 32 | ` 33 | 34 | var ( 35 | addr = flag.String("addr", ":8080", "http service address") 36 | online int32 37 | ) 38 | 39 | func main() { 40 | fmt.Println(logo) 41 | flag.Parse() 42 | static := http.FileServer(http.Dir("./2048")) 43 | http.Handle("/js/", static) 44 | http.Handle("/style/", static) 45 | http.Handle("/meta/", static) 46 | http.Handle("/favicon.ico", static) 47 | 48 | indexTpl := template.Must(template.ParseFiles("./2048/index.html")) 49 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 50 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 51 | indexTpl.Execute(w, nil) 52 | }) 53 | http.HandleFunc("/compute", compute) 54 | 55 | log.Printf("Service started on \x1b[32;1m%s\x1b[32;1m\x1b[0m\n", *addr) 56 | log.Fatal(http.ListenAndServe(*addr, nil)) 57 | } 58 | 59 | func compute(w http.ResponseWriter, r *http.Request) { 60 | conn, err := upgrader.Upgrade(w, r, nil) 61 | if err != nil { 62 | log.Println("upgrade error:", err.Error()) 63 | return 64 | } 65 | defer func() { 66 | if conn != nil { 67 | conn.Close() 68 | } 69 | online = atomic.AddInt32(&online, -1) 70 | log.Println(online) 71 | }() 72 | online = atomic.AddInt32(&online, 1) 73 | log.Println(online) 74 | for { 75 | messageType, p, err := conn.ReadMessage() 76 | if err != nil { 77 | break 78 | } 79 | g := &grid.Grid{} 80 | if err = json.Unmarshal(p, g); err != nil { 81 | break 82 | } 83 | a := &ai.AI{Grid: g} 84 | dire := a.Search() 85 | result := map[string]grid.Direction{"dire": dire} 86 | p, _ = json.Marshal(result) 87 | if err := conn.WriteMessage(messageType, p); err != nil { 88 | break 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /screenshot/2048-ai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjdsh/2048-ai/eb763d73cd786b140b34b7b95e0b2eb1a357161a/screenshot/2048-ai.jpg -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type Point struct { 4 | X, Y int 5 | } 6 | 7 | func Diff(a1, a2 [4][4]int) bool { 8 | for i := 0; i < 4; i++ { 9 | for j := 0; j < 4; j++ { 10 | if a1[i][j] != a2[i][j] { 11 | return true 12 | } 13 | } 14 | } 15 | return false 16 | } 17 | -------------------------------------------------------------------------------- /utils/utils_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestDiff(t *testing.T) { 10 | Convey("Init data", t, func() { 11 | d1 := [4][4]int{ 12 | {2, 2, 0, 4}, 13 | {2, 2, 4, 0}, 14 | {2, 2, 2, 2}, 15 | {2, 2, 4, 4}, 16 | } 17 | d2 := [4][4]int{ 18 | {2, 2, 0, 4}, 19 | {2, 2, 4, 0}, 20 | {2, 2, 2, 2}, 21 | {2, 2, 4, 4}, 22 | } 23 | d3 := [4][4]int{ 24 | {2, 2, 0, 4}, 25 | {2, 2, 2, 0}, 26 | {2, 2, 2, 2}, 27 | {2, 2, 4, 4}, 28 | } 29 | So(Diff(d1, d2), ShouldEqual, false) 30 | So(Diff(d2, d3), ShouldEqual, true) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | [![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) 7 | [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) 8 | 9 | ### Documentation 10 | 11 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 12 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 13 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 14 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 15 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 16 | 17 | ### Status 18 | 19 | The Gorilla WebSocket package provides a complete and tested implementation of 20 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 21 | package API is stable. 22 | 23 | ### Installation 24 | 25 | go get github.com/gorilla/websocket 26 | 27 | ### Protocol Compliance 28 | 29 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 30 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 31 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 32 | 33 | ### Gorilla WebSocket compared with other packages 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
53 | 54 | Notes: 55 | 56 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 57 | 2. The application can get the type of a received data message by implementing 58 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 59 | function. 60 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 61 | Read returns when the input buffer is full or a frame boundary is 62 | encountered. Each call to Write sends a single frame message. The Gorilla 63 | io.Reader and io.WriteCloser operate on a single WebSocket message. 64 | 65 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 12 | if cfg == nil { 13 | return &tls.Config{} 14 | } 15 | return cfg.Clone() 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | // cloneTLSConfig clones all public fields except the fields 12 | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the 13 | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a 14 | // config in active use. 15 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 16 | if cfg == nil { 17 | return &tls.Config{} 18 | } 19 | return &tls.Config{ 20 | Rand: cfg.Rand, 21 | Time: cfg.Time, 22 | Certificates: cfg.Certificates, 23 | NameToCertificate: cfg.NameToCertificate, 24 | GetCertificate: cfg.GetCertificate, 25 | RootCAs: cfg.RootCAs, 26 | NextProtos: cfg.NextProtos, 27 | ServerName: cfg.ServerName, 28 | ClientAuth: cfg.ClientAuth, 29 | ClientCAs: cfg.ClientCAs, 30 | InsecureSkipVerify: cfg.InsecureSkipVerify, 31 | CipherSuites: cfg.CipherSuites, 32 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 33 | ClientSessionCache: cfg.ClientSessionCache, 34 | MinVersion: cfg.MinVersion, 35 | MaxVersion: cfg.MaxVersion, 36 | CurvePreferences: cfg.CurvePreferences, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "compress/flate" 9 | "errors" 10 | "io" 11 | "strings" 12 | "sync" 13 | ) 14 | 15 | const ( 16 | minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 17 | maxCompressionLevel = flate.BestCompression 18 | defaultCompressionLevel = 1 19 | ) 20 | 21 | var ( 22 | flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool 23 | flateReaderPool = sync.Pool{New: func() interface{} { 24 | return flate.NewReader(nil) 25 | }} 26 | ) 27 | 28 | func decompressNoContextTakeover(r io.Reader) io.ReadCloser { 29 | const tail = 30 | // Add four bytes as specified in RFC 31 | "\x00\x00\xff\xff" + 32 | // Add final block to squelch unexpected EOF error from flate reader. 33 | "\x01\x00\x00\xff\xff" 34 | 35 | fr, _ := flateReaderPool.Get().(io.ReadCloser) 36 | fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) 37 | return &flateReadWrapper{fr} 38 | } 39 | 40 | func isValidCompressionLevel(level int) bool { 41 | return minCompressionLevel <= level && level <= maxCompressionLevel 42 | } 43 | 44 | func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { 45 | p := &flateWriterPools[level-minCompressionLevel] 46 | tw := &truncWriter{w: w} 47 | fw, _ := p.Get().(*flate.Writer) 48 | if fw == nil { 49 | fw, _ = flate.NewWriter(tw, level) 50 | } else { 51 | fw.Reset(tw) 52 | } 53 | return &flateWriteWrapper{fw: fw, tw: tw, p: p} 54 | } 55 | 56 | // truncWriter is an io.Writer that writes all but the last four bytes of the 57 | // stream to another io.Writer. 58 | type truncWriter struct { 59 | w io.WriteCloser 60 | n int 61 | p [4]byte 62 | } 63 | 64 | func (w *truncWriter) Write(p []byte) (int, error) { 65 | n := 0 66 | 67 | // fill buffer first for simplicity. 68 | if w.n < len(w.p) { 69 | n = copy(w.p[w.n:], p) 70 | p = p[n:] 71 | w.n += n 72 | if len(p) == 0 { 73 | return n, nil 74 | } 75 | } 76 | 77 | m := len(p) 78 | if m > len(w.p) { 79 | m = len(w.p) 80 | } 81 | 82 | if nn, err := w.w.Write(w.p[:m]); err != nil { 83 | return n + nn, err 84 | } 85 | 86 | copy(w.p[:], w.p[m:]) 87 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 88 | nn, err := w.w.Write(p[:len(p)-m]) 89 | return n + nn, err 90 | } 91 | 92 | type flateWriteWrapper struct { 93 | fw *flate.Writer 94 | tw *truncWriter 95 | p *sync.Pool 96 | } 97 | 98 | func (w *flateWriteWrapper) Write(p []byte) (int, error) { 99 | if w.fw == nil { 100 | return 0, errWriteClosed 101 | } 102 | return w.fw.Write(p) 103 | } 104 | 105 | func (w *flateWriteWrapper) Close() error { 106 | if w.fw == nil { 107 | return errWriteClosed 108 | } 109 | err1 := w.fw.Flush() 110 | w.p.Put(w.fw) 111 | w.fw = nil 112 | if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { 113 | return errors.New("websocket: internal error, unexpected bytes at end of flate stream") 114 | } 115 | err2 := w.tw.w.Close() 116 | if err1 != nil { 117 | return err1 118 | } 119 | return err2 120 | } 121 | 122 | type flateReadWrapper struct { 123 | fr io.ReadCloser 124 | } 125 | 126 | func (r *flateReadWrapper) Read(p []byte) (int, error) { 127 | if r.fr == nil { 128 | return 0, io.ErrClosedPipe 129 | } 130 | n, err := r.fr.Read(p) 131 | if err == io.EOF { 132 | // Preemptively place the reader back in the pool. This helps with 133 | // scenarios where the application does not call NextReader() soon after 134 | // this final read. 135 | r.Close() 136 | } 137 | return n, err 138 | } 139 | 140 | func (r *flateReadWrapper) Close() error { 141 | if r.fr == nil { 142 | return io.ErrClosedPipe 143 | } 144 | err := r.fr.Close() 145 | flateReaderPool.Put(r.fr) 146 | r.fr = nil 147 | return err 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | c.br.Discard(len(p)) 17 | return p, err 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | if len(p) > 0 { 17 | // advance over the bytes just read 18 | io.ReadFull(c.br, p) 19 | } 20 | return p, err 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package websocket implements the WebSocket protocol defined in RFC 6455. 6 | // 7 | // Overview 8 | // 9 | // The Conn type represents a WebSocket connection. A server application calls 10 | // the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: 11 | // 12 | // var upgrader = websocket.Upgrader{ 13 | // ReadBufferSize: 1024, 14 | // WriteBufferSize: 1024, 15 | // } 16 | // 17 | // func handler(w http.ResponseWriter, r *http.Request) { 18 | // conn, err := upgrader.Upgrade(w, r, nil) 19 | // if err != nil { 20 | // log.Println(err) 21 | // return 22 | // } 23 | // ... Use conn to send and receive messages. 24 | // } 25 | // 26 | // Call the connection's WriteMessage and ReadMessage methods to send and 27 | // receive messages as a slice of bytes. This snippet of code shows how to echo 28 | // messages using these methods: 29 | // 30 | // for { 31 | // messageType, p, err := conn.ReadMessage() 32 | // if err != nil { 33 | // return 34 | // } 35 | // if err := conn.WriteMessage(messageType, p); err != nil { 36 | // return err 37 | // } 38 | // } 39 | // 40 | // In above snippet of code, p is a []byte and messageType is an int with value 41 | // websocket.BinaryMessage or websocket.TextMessage. 42 | // 43 | // An application can also send and receive messages using the io.WriteCloser 44 | // and io.Reader interfaces. To send a message, call the connection NextWriter 45 | // method to get an io.WriteCloser, write the message to the writer and close 46 | // the writer when done. To receive a message, call the connection NextReader 47 | // method to get an io.Reader and read until io.EOF is returned. This snippet 48 | // shows how to echo messages using the NextWriter and NextReader methods: 49 | // 50 | // for { 51 | // messageType, r, err := conn.NextReader() 52 | // if err != nil { 53 | // return 54 | // } 55 | // w, err := conn.NextWriter(messageType) 56 | // if err != nil { 57 | // return err 58 | // } 59 | // if _, err := io.Copy(w, r); err != nil { 60 | // return err 61 | // } 62 | // if err := w.Close(); err != nil { 63 | // return err 64 | // } 65 | // } 66 | // 67 | // Data Messages 68 | // 69 | // The WebSocket protocol distinguishes between text and binary data messages. 70 | // Text messages are interpreted as UTF-8 encoded text. The interpretation of 71 | // binary messages is left to the application. 72 | // 73 | // This package uses the TextMessage and BinaryMessage integer constants to 74 | // identify the two data message types. The ReadMessage and NextReader methods 75 | // return the type of the received message. The messageType argument to the 76 | // WriteMessage and NextWriter methods specifies the type of a sent message. 77 | // 78 | // It is the application's responsibility to ensure that text messages are 79 | // valid UTF-8 encoded text. 80 | // 81 | // Control Messages 82 | // 83 | // The WebSocket protocol defines three types of control messages: close, ping 84 | // and pong. Call the connection WriteControl, WriteMessage or NextWriter 85 | // methods to send a control message to the peer. 86 | // 87 | // Connections handle received close messages by sending a close message to the 88 | // peer and returning a *CloseError from the the NextReader, ReadMessage or the 89 | // message Read method. 90 | // 91 | // Connections handle received ping and pong messages by invoking callback 92 | // functions set with SetPingHandler and SetPongHandler methods. The callback 93 | // functions are called from the NextReader, ReadMessage and the message Read 94 | // methods. 95 | // 96 | // The default ping handler sends a pong to the peer. The application's reading 97 | // goroutine can block for a short time while the handler writes the pong data 98 | // to the connection. 99 | // 100 | // The application must read the connection to process ping, pong and close 101 | // messages sent from the peer. If the application is not otherwise interested 102 | // in messages from the peer, then the application should start a goroutine to 103 | // read and discard messages from the peer. A simple example is: 104 | // 105 | // func readLoop(c *websocket.Conn) { 106 | // for { 107 | // if _, _, err := c.NextReader(); err != nil { 108 | // c.Close() 109 | // break 110 | // } 111 | // } 112 | // } 113 | // 114 | // Concurrency 115 | // 116 | // Connections support one concurrent reader and one concurrent writer. 117 | // 118 | // Applications are responsible for ensuring that no more than one goroutine 119 | // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, 120 | // WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and 121 | // that no more than one goroutine calls the read methods (NextReader, 122 | // SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) 123 | // concurrently. 124 | // 125 | // The Close and WriteControl methods can be called concurrently with all other 126 | // methods. 127 | // 128 | // Origin Considerations 129 | // 130 | // Web browsers allow Javascript applications to open a WebSocket connection to 131 | // any host. It's up to the server to enforce an origin policy using the Origin 132 | // request header sent by the browser. 133 | // 134 | // The Upgrader calls the function specified in the CheckOrigin field to check 135 | // the origin. If the CheckOrigin function returns false, then the Upgrade 136 | // method fails the WebSocket handshake with HTTP status 403. 137 | // 138 | // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail 139 | // the handshake if the Origin request header is present and not equal to the 140 | // Host request header. 141 | // 142 | // An application can allow connections from any origin by specifying a 143 | // function that always returns true: 144 | // 145 | // var upgrader = websocket.Upgrader{ 146 | // CheckOrigin: func(r *http.Request) bool { return true }, 147 | // } 148 | // 149 | // The deprecated package-level Upgrade function does not perform origin 150 | // checking. The application is responsible for checking the Origin header 151 | // before calling the Upgrade function. 152 | // 153 | // Compression EXPERIMENTAL 154 | // 155 | // Per message compression extensions (RFC 7692) are experimentally supported 156 | // by this package in a limited capacity. Setting the EnableCompression option 157 | // to true in Dialer or Upgrader will attempt to negotiate per message deflate 158 | // support. 159 | // 160 | // var upgrader = websocket.Upgrader{ 161 | // EnableCompression: true, 162 | // } 163 | // 164 | // If compression was successfully negotiated with the connection's peer, any 165 | // message received in compressed form will be automatically decompressed. 166 | // All Read methods will return uncompressed bytes. 167 | // 168 | // Per message compression of messages written to a connection can be enabled 169 | // or disabled by calling the corresponding Conn method: 170 | // 171 | // conn.EnableWriteCompression(false) 172 | // 173 | // Currently this package does not support compression with "context takeover". 174 | // This means that messages must be compressed and decompressed in isolation, 175 | // without retaining sliding window or dictionary state across messages. For 176 | // more details refer to RFC 7692. 177 | // 178 | // Use of compression is experimental and may result in decreased performance. 179 | package websocket 180 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON writes the JSON encoding of v as a message. 13 | // 14 | // Deprecated: Use c.WriteJSON instead. 15 | func WriteJSON(c *Conn, v interface{}) error { 16 | return c.WriteJSON(v) 17 | } 18 | 19 | // WriteJSON writes the JSON encoding of v as a message. 20 | // 21 | // See the documentation for encoding/json Marshal for details about the 22 | // conversion of Go values to JSON. 23 | func (c *Conn) WriteJSON(v interface{}) error { 24 | w, err := c.NextWriter(TextMessage) 25 | if err != nil { 26 | return err 27 | } 28 | err1 := json.NewEncoder(w).Encode(v) 29 | err2 := w.Close() 30 | if err1 != nil { 31 | return err1 32 | } 33 | return err2 34 | } 35 | 36 | // ReadJSON reads the next JSON-encoded message from the connection and stores 37 | // it in the value pointed to by v. 38 | // 39 | // Deprecated: Use c.ReadJSON instead. 40 | func ReadJSON(c *Conn, v interface{}) error { 41 | return c.ReadJSON(v) 42 | } 43 | 44 | // ReadJSON reads the next JSON-encoded message from the connection and stores 45 | // it in the value pointed to by v. 46 | // 47 | // See the documentation for the encoding/json Unmarshal function for details 48 | // about the conversion of JSON to a Go value. 49 | func (c *Conn) ReadJSON(v interface{}) error { 50 | _, r, err := c.NextReader() 51 | if err != nil { 52 | return err 53 | } 54 | err = json.NewDecoder(r).Decode(v) 55 | if err == io.EOF { 56 | // One value is expected in the message. 57 | err = io.ErrUnexpectedEOF 58 | } 59 | return err 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | package websocket 8 | 9 | import "unsafe" 10 | 11 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 12 | 13 | func maskBytes(key [4]byte, pos int, b []byte) int { 14 | 15 | // Mask one byte at a time for small buffers. 16 | if len(b) < 2*wordSize { 17 | for i := range b { 18 | b[i] ^= key[pos&3] 19 | pos++ 20 | } 21 | return pos & 3 22 | } 23 | 24 | // Mask one byte at a time to word boundary. 25 | if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { 26 | n = wordSize - n 27 | for i := range b[:n] { 28 | b[i] ^= key[pos&3] 29 | pos++ 30 | } 31 | b = b[n:] 32 | } 33 | 34 | // Create aligned word size key. 35 | var k [wordSize]byte 36 | for i := range k { 37 | k[i] = key[(pos+i)&3] 38 | } 39 | kw := *(*uintptr)(unsafe.Pointer(&k)) 40 | 41 | // Mask one word at a time. 42 | n := (len(b) / wordSize) * wordSize 43 | for i := 0; i < n; i += wordSize { 44 | *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw 45 | } 46 | 47 | // Mask one byte at a time for remaining bytes. 48 | b = b[n:] 49 | for i := range b { 50 | b[i] ^= key[pos&3] 51 | pos++ 52 | } 53 | 54 | return pos & 3 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask_safe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build appengine 6 | 7 | package websocket 8 | 9 | func maskBytes(key [4]byte, pos int, b []byte) int { 10 | for i := range b { 11 | b[i] ^= key[pos&3] 12 | pos++ 13 | } 14 | return pos & 3 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/prepared.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "net" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // PreparedMessage caches on the wire representations of a message payload. 15 | // Use PreparedMessage to efficiently send a message payload to multiple 16 | // connections. PreparedMessage is especially useful when compression is used 17 | // because the CPU and memory expensive compression operation can be executed 18 | // once for a given set of compression options. 19 | type PreparedMessage struct { 20 | messageType int 21 | data []byte 22 | err error 23 | mu sync.Mutex 24 | frames map[prepareKey]*preparedFrame 25 | } 26 | 27 | // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. 28 | type prepareKey struct { 29 | isServer bool 30 | compress bool 31 | compressionLevel int 32 | } 33 | 34 | // preparedFrame contains data in wire representation. 35 | type preparedFrame struct { 36 | once sync.Once 37 | data []byte 38 | } 39 | 40 | // NewPreparedMessage returns an initialized PreparedMessage. You can then send 41 | // it to connection using WritePreparedMessage method. Valid wire 42 | // representation will be calculated lazily only once for a set of current 43 | // connection options. 44 | func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { 45 | pm := &PreparedMessage{ 46 | messageType: messageType, 47 | frames: make(map[prepareKey]*preparedFrame), 48 | data: data, 49 | } 50 | 51 | // Prepare a plain server frame. 52 | _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | // To protect against caller modifying the data argument, remember the data 58 | // copied to the plain server frame. 59 | pm.data = frameData[len(frameData)-len(data):] 60 | return pm, nil 61 | } 62 | 63 | func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { 64 | pm.mu.Lock() 65 | frame, ok := pm.frames[key] 66 | if !ok { 67 | frame = &preparedFrame{} 68 | pm.frames[key] = frame 69 | } 70 | pm.mu.Unlock() 71 | 72 | var err error 73 | frame.once.Do(func() { 74 | // Prepare a frame using a 'fake' connection. 75 | // TODO: Refactor code in conn.go to allow more direct construction of 76 | // the frame. 77 | mu := make(chan bool, 1) 78 | mu <- true 79 | var nc prepareConn 80 | c := &Conn{ 81 | conn: &nc, 82 | mu: mu, 83 | isServer: key.isServer, 84 | compressionLevel: key.compressionLevel, 85 | enableWriteCompression: true, 86 | writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), 87 | } 88 | if key.compress { 89 | c.newCompressionWriter = compressNoContextTakeover 90 | } 91 | err = c.WriteMessage(pm.messageType, pm.data) 92 | frame.data = nc.buf.Bytes() 93 | }) 94 | return pm.messageType, frame.data, err 95 | } 96 | 97 | type prepareConn struct { 98 | buf bytes.Buffer 99 | net.Conn 100 | } 101 | 102 | func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } 103 | func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } 104 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 17 | 18 | func computeAcceptKey(challengeKey string) string { 19 | h := sha1.New() 20 | h.Write([]byte(challengeKey)) 21 | h.Write(keyGUID) 22 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 23 | } 24 | 25 | func generateChallengeKey() (string, error) { 26 | p := make([]byte, 16) 27 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 28 | return "", err 29 | } 30 | return base64.StdEncoding.EncodeToString(p), nil 31 | } 32 | 33 | // Octet types from RFC 2616. 34 | var octetTypes [256]byte 35 | 36 | const ( 37 | isTokenOctet = 1 << iota 38 | isSpaceOctet 39 | ) 40 | 41 | func init() { 42 | // From RFC 2616 43 | // 44 | // OCTET = 45 | // CHAR = 46 | // CTL = 47 | // CR = 48 | // LF = 49 | // SP = 50 | // HT = 51 | // <"> = 52 | // CRLF = CR LF 53 | // LWS = [CRLF] 1*( SP | HT ) 54 | // TEXT = 55 | // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 56 | // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 57 | // token = 1* 58 | // qdtext = > 59 | 60 | for c := 0; c < 256; c++ { 61 | var t byte 62 | isCtl := c <= 31 || c == 127 63 | isChar := 0 <= c && c <= 127 64 | isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 65 | if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { 66 | t |= isSpaceOctet 67 | } 68 | if isChar && !isCtl && !isSeparator { 69 | t |= isTokenOctet 70 | } 71 | octetTypes[c] = t 72 | } 73 | } 74 | 75 | func skipSpace(s string) (rest string) { 76 | i := 0 77 | for ; i < len(s); i++ { 78 | if octetTypes[s[i]]&isSpaceOctet == 0 { 79 | break 80 | } 81 | } 82 | return s[i:] 83 | } 84 | 85 | func nextToken(s string) (token, rest string) { 86 | i := 0 87 | for ; i < len(s); i++ { 88 | if octetTypes[s[i]]&isTokenOctet == 0 { 89 | break 90 | } 91 | } 92 | return s[:i], s[i:] 93 | } 94 | 95 | func nextTokenOrQuoted(s string) (value string, rest string) { 96 | if !strings.HasPrefix(s, "\"") { 97 | return nextToken(s) 98 | } 99 | s = s[1:] 100 | for i := 0; i < len(s); i++ { 101 | switch s[i] { 102 | case '"': 103 | return s[:i], s[i+1:] 104 | case '\\': 105 | p := make([]byte, len(s)-1) 106 | j := copy(p, s[:i]) 107 | escape := true 108 | for i = i + 1; i < len(s); i++ { 109 | b := s[i] 110 | switch { 111 | case escape: 112 | escape = false 113 | p[j] = b 114 | j++ 115 | case b == '\\': 116 | escape = true 117 | case b == '"': 118 | return string(p[:j]), s[i+1:] 119 | default: 120 | p[j] = b 121 | j++ 122 | } 123 | } 124 | return "", "" 125 | } 126 | } 127 | return "", "" 128 | } 129 | 130 | // tokenListContainsValue returns true if the 1#token header with the given 131 | // name contains token. 132 | func tokenListContainsValue(header http.Header, name string, value string) bool { 133 | headers: 134 | for _, s := range header[name] { 135 | for { 136 | var t string 137 | t, s = nextToken(skipSpace(s)) 138 | if t == "" { 139 | continue headers 140 | } 141 | s = skipSpace(s) 142 | if s != "" && s[0] != ',' { 143 | continue headers 144 | } 145 | if strings.EqualFold(t, value) { 146 | return true 147 | } 148 | if s == "" { 149 | continue headers 150 | } 151 | s = s[1:] 152 | } 153 | } 154 | return false 155 | } 156 | 157 | // parseExtensiosn parses WebSocket extensions from a header. 158 | func parseExtensions(header http.Header) []map[string]string { 159 | 160 | // From RFC 6455: 161 | // 162 | // Sec-WebSocket-Extensions = extension-list 163 | // extension-list = 1#extension 164 | // extension = extension-token *( ";" extension-param ) 165 | // extension-token = registered-token 166 | // registered-token = token 167 | // extension-param = token [ "=" (token | quoted-string) ] 168 | // ;When using the quoted-string syntax variant, the value 169 | // ;after quoted-string unescaping MUST conform to the 170 | // ;'token' ABNF. 171 | 172 | var result []map[string]string 173 | headers: 174 | for _, s := range header["Sec-Websocket-Extensions"] { 175 | for { 176 | var t string 177 | t, s = nextToken(skipSpace(s)) 178 | if t == "" { 179 | continue headers 180 | } 181 | ext := map[string]string{"": t} 182 | for { 183 | s = skipSpace(s) 184 | if !strings.HasPrefix(s, ";") { 185 | break 186 | } 187 | var k string 188 | k, s = nextToken(skipSpace(s[1:])) 189 | if k == "" { 190 | continue headers 191 | } 192 | s = skipSpace(s) 193 | var v string 194 | if strings.HasPrefix(s, "=") { 195 | v, s = nextTokenOrQuoted(skipSpace(s[1:])) 196 | s = skipSpace(s) 197 | } 198 | if s != "" && s[0] != ',' && s[0] != ';' { 199 | continue headers 200 | } 201 | ext[k] = v 202 | } 203 | if s != "" && s[0] != ',' { 204 | continue headers 205 | } 206 | result = append(result, ext) 207 | if s == "" { 208 | continue headers 209 | } 210 | s = s[1:] 211 | } 212 | } 213 | return result 214 | } 215 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Space Monkey, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/README.md: -------------------------------------------------------------------------------- 1 | gls 2 | === 3 | 4 | Goroutine local storage 5 | 6 | ### IMPORTANT NOTE ### 7 | 8 | It is my duty to point you to https://blog.golang.org/context, which is how 9 | Google solves all of the problems you'd perhaps consider using this package 10 | for at scale. 11 | 12 | One downside to Google's approach is that *all* of your functions must have 13 | a new first argument, but after clearing that hurdle everything else is much 14 | better. 15 | 16 | If you aren't interested in this warning, read on. 17 | 18 | ### Huhwaht? Why? ### 19 | 20 | Every so often, a thread shows up on the 21 | [golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some 22 | form of goroutine-local-storage, or some kind of goroutine id, or some kind of 23 | context. There are a few valid use cases for goroutine-local-storage, one of 24 | the most prominent being log line context. One poster was interested in being 25 | able to log an HTTP request context id in every log line in the same goroutine 26 | as the incoming HTTP request, without having to change every library and 27 | function call he was interested in logging. 28 | 29 | This would be pretty useful. Provided that you could get some kind of 30 | goroutine-local-storage, you could call 31 | [log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging 32 | writer that checks goroutine-local-storage for some context information and 33 | adds that context to your log lines. 34 | 35 | But alas, Andrew Gerrand's typically diplomatic answer to the question of 36 | goroutine-local variables was: 37 | 38 | > We wouldn't even be having this discussion if thread local storage wasn't 39 | > useful. But every feature comes at a cost, and in my opinion the cost of 40 | > threadlocals far outweighs their benefits. They're just not a good fit for 41 | > Go. 42 | 43 | So, yeah, that makes sense. That's a pretty good reason for why the language 44 | won't support a specific and (relatively) unuseful feature that requires some 45 | runtime changes, just for the sake of a little bit of log improvement. 46 | 47 | But does Go require runtime changes? 48 | 49 | ### How it works ### 50 | 51 | Go has pretty fantastic introspective and reflective features, but one thing Go 52 | doesn't give you is any kind of access to the stack pointer, or frame pointer, 53 | or goroutine id, or anything contextual about your current stack. It gives you 54 | access to your list of callers, but only along with program counters, which are 55 | fixed at compile time. 56 | 57 | But it does give you the stack. 58 | 59 | So, we define 16 special functions and embed base-16 tags into the stack using 60 | the call order of those 16 functions. Then, we can read our tags back out of 61 | the stack looking at the callers list. 62 | 63 | We then use these tags as an index into a traditional map for implementing 64 | this library. 65 | 66 | ### What are people saying? ### 67 | 68 | "Wow, that's horrifying." 69 | 70 | "This is the most terrible thing I have seen in a very long time." 71 | 72 | "Where is it getting a context from? Is this serializing all the requests? 73 | What the heck is the client being bound to? What are these tags? Why does he 74 | need callers? Oh god no. No no no." 75 | 76 | ### Docs ### 77 | 78 | Please see the docs at http://godoc.org/github.com/jtolds/gls 79 | 80 | ### Related ### 81 | 82 | If you're okay relying on the string format of the current runtime stacktrace 83 | including a unique goroutine id (not guaranteed by the spec or anything, but 84 | very unlikely to change within a Go release), you might be able to squeeze 85 | out a bit more performance by using this similar library, inspired by some 86 | code Brad Fitzpatrick wrote for debugging his HTTP/2 library: 87 | https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require 88 | any knowledge of the string format of the runtime stacktrace, which 89 | probably adds unnecessary overhead). 90 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/context.go: -------------------------------------------------------------------------------- 1 | // Package gls implements goroutine-local storage. 2 | package gls 3 | 4 | import ( 5 | "sync" 6 | ) 7 | 8 | var ( 9 | mgrRegistry = make(map[*ContextManager]bool) 10 | mgrRegistryMtx sync.RWMutex 11 | ) 12 | 13 | // Values is simply a map of key types to value types. Used by SetValues to 14 | // set multiple values at once. 15 | type Values map[interface{}]interface{} 16 | 17 | // ContextManager is the main entrypoint for interacting with 18 | // Goroutine-local-storage. You can have multiple independent ContextManagers 19 | // at any given time. ContextManagers are usually declared globally for a given 20 | // class of context variables. You should use NewContextManager for 21 | // construction. 22 | type ContextManager struct { 23 | mtx sync.Mutex 24 | values map[uint]Values 25 | } 26 | 27 | // NewContextManager returns a brand new ContextManager. It also registers the 28 | // new ContextManager in the ContextManager registry which is used by the Go 29 | // method. ContextManagers are typically defined globally at package scope. 30 | func NewContextManager() *ContextManager { 31 | mgr := &ContextManager{values: make(map[uint]Values)} 32 | mgrRegistryMtx.Lock() 33 | defer mgrRegistryMtx.Unlock() 34 | mgrRegistry[mgr] = true 35 | return mgr 36 | } 37 | 38 | // Unregister removes a ContextManager from the global registry, used by the 39 | // Go method. Only intended for use when you're completely done with a 40 | // ContextManager. Use of Unregister at all is rare. 41 | func (m *ContextManager) Unregister() { 42 | mgrRegistryMtx.Lock() 43 | defer mgrRegistryMtx.Unlock() 44 | delete(mgrRegistry, m) 45 | } 46 | 47 | // SetValues takes a collection of values and a function to call for those 48 | // values to be set in. Anything further down the stack will have the set 49 | // values available through GetValue. SetValues will add new values or replace 50 | // existing values of the same key and will not mutate or change values for 51 | // previous stack frames. 52 | // SetValues is slow (makes a copy of all current and new values for the new 53 | // gls-context) in order to reduce the amount of lookups GetValue requires. 54 | func (m *ContextManager) SetValues(new_values Values, context_call func()) { 55 | if len(new_values) == 0 { 56 | context_call() 57 | return 58 | } 59 | 60 | mutated_keys := make([]interface{}, 0, len(new_values)) 61 | mutated_vals := make(Values, len(new_values)) 62 | 63 | EnsureGoroutineId(func(gid uint) { 64 | m.mtx.Lock() 65 | state, found := m.values[gid] 66 | if !found { 67 | state = make(Values, len(new_values)) 68 | m.values[gid] = state 69 | } 70 | m.mtx.Unlock() 71 | 72 | for key, new_val := range new_values { 73 | mutated_keys = append(mutated_keys, key) 74 | if old_val, ok := state[key]; ok { 75 | mutated_vals[key] = old_val 76 | } 77 | state[key] = new_val 78 | } 79 | 80 | defer func() { 81 | if !found { 82 | m.mtx.Lock() 83 | delete(m.values, gid) 84 | m.mtx.Unlock() 85 | return 86 | } 87 | 88 | for _, key := range mutated_keys { 89 | if val, ok := mutated_vals[key]; ok { 90 | state[key] = val 91 | } else { 92 | delete(state, key) 93 | } 94 | } 95 | }() 96 | 97 | context_call() 98 | }) 99 | } 100 | 101 | // GetValue will return a previously set value, provided that the value was set 102 | // by SetValues somewhere higher up the stack. If the value is not found, ok 103 | // will be false. 104 | func (m *ContextManager) GetValue(key interface{}) ( 105 | value interface{}, ok bool) { 106 | gid, ok := GetGoroutineId() 107 | if !ok { 108 | return nil, false 109 | } 110 | 111 | m.mtx.Lock() 112 | state, found := m.values[gid] 113 | m.mtx.Unlock() 114 | 115 | if !found { 116 | return nil, false 117 | } 118 | value, ok = state[key] 119 | return value, ok 120 | } 121 | 122 | func (m *ContextManager) getValues() Values { 123 | gid, ok := GetGoroutineId() 124 | if !ok { 125 | return nil 126 | } 127 | m.mtx.Lock() 128 | state, _ := m.values[gid] 129 | m.mtx.Unlock() 130 | return state 131 | } 132 | 133 | // Go preserves ContextManager values and Goroutine-local-storage across new 134 | // goroutine invocations. The Go method makes a copy of all existing values on 135 | // all registered context managers and makes sure they are still set after 136 | // kicking off the provided function in a new goroutine. If you don't use this 137 | // Go method instead of the standard 'go' keyword, you will lose values in 138 | // ContextManagers, as goroutines have brand new stacks. 139 | func Go(cb func()) { 140 | mgrRegistryMtx.RLock() 141 | defer mgrRegistryMtx.RUnlock() 142 | 143 | for mgr := range mgrRegistry { 144 | values := mgr.getValues() 145 | if len(values) > 0 { 146 | cb = func(mgr *ContextManager, cb func()) func() { 147 | return func() { mgr.SetValues(values, cb) } 148 | }(mgr, cb) 149 | } 150 | } 151 | 152 | go cb() 153 | } 154 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/gen_sym.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var ( 8 | keyMtx sync.Mutex 9 | keyCounter uint64 10 | ) 11 | 12 | // ContextKey is a throwaway value you can use as a key to a ContextManager 13 | type ContextKey struct{ id uint64 } 14 | 15 | // GenSym will return a brand new, never-before-used ContextKey 16 | func GenSym() ContextKey { 17 | keyMtx.Lock() 18 | defer keyMtx.Unlock() 19 | keyCounter += 1 20 | return ContextKey{id: keyCounter} 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/gid.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | var ( 4 | stackTagPool = &idPool{} 5 | ) 6 | 7 | // Will return this goroutine's identifier if set. If you always need a 8 | // goroutine identifier, you should use EnsureGoroutineId which will make one 9 | // if there isn't one already. 10 | func GetGoroutineId() (gid uint, ok bool) { 11 | return readStackTag() 12 | } 13 | 14 | // Will call cb with the current goroutine identifier. If one hasn't already 15 | // been generated, one will be created and set first. The goroutine identifier 16 | // might be invalid after cb returns. 17 | func EnsureGoroutineId(cb func(gid uint)) { 18 | if gid, ok := readStackTag(); ok { 19 | cb(gid) 20 | return 21 | } 22 | gid := stackTagPool.Acquire() 23 | defer stackTagPool.Release(gid) 24 | addStackTag(gid, func() { cb(gid) }) 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/id_pool.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | // though this could probably be better at keeping ids smaller, the goal of 4 | // this class is to keep a registry of the smallest unique integer ids 5 | // per-process possible 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | type idPool struct { 12 | mtx sync.Mutex 13 | released []uint 14 | max_id uint 15 | } 16 | 17 | func (p *idPool) Acquire() (id uint) { 18 | p.mtx.Lock() 19 | defer p.mtx.Unlock() 20 | if len(p.released) > 0 { 21 | id = p.released[len(p.released)-1] 22 | p.released = p.released[:len(p.released)-1] 23 | return id 24 | } 25 | id = p.max_id 26 | p.max_id++ 27 | return id 28 | } 29 | 30 | func (p *idPool) Release(id uint) { 31 | p.mtx.Lock() 32 | defer p.mtx.Unlock() 33 | p.released = append(p.released, id) 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/stack_tags.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | // so, basically, we're going to encode integer tags in base-16 on the stack 4 | 5 | const ( 6 | bitWidth = 4 7 | stackBatchSize = 16 8 | ) 9 | 10 | var ( 11 | pc_lookup = make(map[uintptr]int8, 17) 12 | mark_lookup [16]func(uint, func()) 13 | ) 14 | 15 | func init() { 16 | setEntries := func(f func(uint, func()), v int8) { 17 | var ptr uintptr 18 | f(0, func() { 19 | ptr = findPtr() 20 | }) 21 | pc_lookup[ptr] = v 22 | if v >= 0 { 23 | mark_lookup[v] = f 24 | } 25 | } 26 | setEntries(github_com_jtolds_gls_markS, -0x1) 27 | setEntries(github_com_jtolds_gls_mark0, 0x0) 28 | setEntries(github_com_jtolds_gls_mark1, 0x1) 29 | setEntries(github_com_jtolds_gls_mark2, 0x2) 30 | setEntries(github_com_jtolds_gls_mark3, 0x3) 31 | setEntries(github_com_jtolds_gls_mark4, 0x4) 32 | setEntries(github_com_jtolds_gls_mark5, 0x5) 33 | setEntries(github_com_jtolds_gls_mark6, 0x6) 34 | setEntries(github_com_jtolds_gls_mark7, 0x7) 35 | setEntries(github_com_jtolds_gls_mark8, 0x8) 36 | setEntries(github_com_jtolds_gls_mark9, 0x9) 37 | setEntries(github_com_jtolds_gls_markA, 0xa) 38 | setEntries(github_com_jtolds_gls_markB, 0xb) 39 | setEntries(github_com_jtolds_gls_markC, 0xc) 40 | setEntries(github_com_jtolds_gls_markD, 0xd) 41 | setEntries(github_com_jtolds_gls_markE, 0xe) 42 | setEntries(github_com_jtolds_gls_markF, 0xf) 43 | } 44 | 45 | func addStackTag(tag uint, context_call func()) { 46 | if context_call == nil { 47 | return 48 | } 49 | github_com_jtolds_gls_markS(tag, context_call) 50 | } 51 | 52 | // these private methods are named this horrendous name so gopherjs support 53 | // is easier. it shouldn't add any runtime cost in non-js builds. 54 | func github_com_jtolds_gls_markS(tag uint, cb func()) { _m(tag, cb) } 55 | func github_com_jtolds_gls_mark0(tag uint, cb func()) { _m(tag, cb) } 56 | func github_com_jtolds_gls_mark1(tag uint, cb func()) { _m(tag, cb) } 57 | func github_com_jtolds_gls_mark2(tag uint, cb func()) { _m(tag, cb) } 58 | func github_com_jtolds_gls_mark3(tag uint, cb func()) { _m(tag, cb) } 59 | func github_com_jtolds_gls_mark4(tag uint, cb func()) { _m(tag, cb) } 60 | func github_com_jtolds_gls_mark5(tag uint, cb func()) { _m(tag, cb) } 61 | func github_com_jtolds_gls_mark6(tag uint, cb func()) { _m(tag, cb) } 62 | func github_com_jtolds_gls_mark7(tag uint, cb func()) { _m(tag, cb) } 63 | func github_com_jtolds_gls_mark8(tag uint, cb func()) { _m(tag, cb) } 64 | func github_com_jtolds_gls_mark9(tag uint, cb func()) { _m(tag, cb) } 65 | func github_com_jtolds_gls_markA(tag uint, cb func()) { _m(tag, cb) } 66 | func github_com_jtolds_gls_markB(tag uint, cb func()) { _m(tag, cb) } 67 | func github_com_jtolds_gls_markC(tag uint, cb func()) { _m(tag, cb) } 68 | func github_com_jtolds_gls_markD(tag uint, cb func()) { _m(tag, cb) } 69 | func github_com_jtolds_gls_markE(tag uint, cb func()) { _m(tag, cb) } 70 | func github_com_jtolds_gls_markF(tag uint, cb func()) { _m(tag, cb) } 71 | 72 | func _m(tag_remainder uint, cb func()) { 73 | if tag_remainder == 0 { 74 | cb() 75 | } else { 76 | mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb) 77 | } 78 | } 79 | 80 | func readStackTag() (tag uint, ok bool) { 81 | var current_tag uint 82 | offset := 0 83 | for { 84 | batch, next_offset := getStack(offset, stackBatchSize) 85 | for _, pc := range batch { 86 | val, ok := pc_lookup[pc] 87 | if !ok { 88 | continue 89 | } 90 | if val < 0 { 91 | return current_tag, true 92 | } 93 | current_tag <<= bitWidth 94 | current_tag += uint(val) 95 | } 96 | if next_offset == 0 { 97 | break 98 | } 99 | offset = next_offset 100 | } 101 | return 0, false 102 | } 103 | 104 | func (m *ContextManager) preventInlining() { 105 | // dunno if findPtr or getStack are likely to get inlined in a future release 106 | // of go, but if they are inlined and their callers are inlined, that could 107 | // hork some things. let's do our best to explain to the compiler that we 108 | // really don't want those two functions inlined by saying they could change 109 | // at any time. assumes preventInlining doesn't get compiled out. 110 | // this whole thing is probably overkill. 111 | findPtr = m.values[0][0].(func() uintptr) 112 | getStack = m.values[0][1].(func(int, int) ([]uintptr, int)) 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/stack_tags_js.go: -------------------------------------------------------------------------------- 1 | // +build js 2 | 3 | package gls 4 | 5 | // This file is used for GopherJS builds, which don't have normal runtime 6 | // stack trace support 7 | 8 | import ( 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/gopherjs/gopherjs/js" 13 | ) 14 | 15 | const ( 16 | jsFuncNamePrefix = "github_com_jtolds_gls_mark" 17 | ) 18 | 19 | func jsMarkStack() (f []uintptr) { 20 | lines := strings.Split( 21 | js.Global.Get("Error").New().Get("stack").String(), "\n") 22 | f = make([]uintptr, 0, len(lines)) 23 | for i, line := range lines { 24 | line = strings.TrimSpace(line) 25 | if line == "" { 26 | continue 27 | } 28 | if i == 0 { 29 | if line != "Error" { 30 | panic("didn't understand js stack trace") 31 | } 32 | continue 33 | } 34 | fields := strings.Fields(line) 35 | if len(fields) < 2 || fields[0] != "at" { 36 | panic("didn't understand js stack trace") 37 | } 38 | 39 | pos := strings.Index(fields[1], jsFuncNamePrefix) 40 | if pos < 0 { 41 | continue 42 | } 43 | pos += len(jsFuncNamePrefix) 44 | if pos >= len(fields[1]) { 45 | panic("didn't understand js stack trace") 46 | } 47 | char := string(fields[1][pos]) 48 | switch char { 49 | case "S": 50 | f = append(f, uintptr(0)) 51 | default: 52 | val, err := strconv.ParseUint(char, 16, 8) 53 | if err != nil { 54 | panic("didn't understand js stack trace") 55 | } 56 | f = append(f, uintptr(val)+1) 57 | } 58 | } 59 | return f 60 | } 61 | 62 | // variables to prevent inlining 63 | var ( 64 | findPtr = func() uintptr { 65 | funcs := jsMarkStack() 66 | if len(funcs) == 0 { 67 | panic("failed to find function pointer") 68 | } 69 | return funcs[0] 70 | } 71 | 72 | getStack = func(offset, amount int) (stack []uintptr, next_offset int) { 73 | return jsMarkStack(), 0 74 | } 75 | ) 76 | -------------------------------------------------------------------------------- /vendor/github.com/jtolds/gls/stack_tags_main.go: -------------------------------------------------------------------------------- 1 | // +build !js 2 | 3 | package gls 4 | 5 | // This file is used for standard Go builds, which have the expected runtime 6 | // support 7 | 8 | import ( 9 | "runtime" 10 | ) 11 | 12 | var ( 13 | findPtr = func() uintptr { 14 | var pc [1]uintptr 15 | n := runtime.Callers(4, pc[:]) 16 | if n != 1 { 17 | panic("failed to find function pointer") 18 | } 19 | return pc[0] 20 | } 21 | 22 | getStack = func(offset, amount int) (stack []uintptr, next_offset int) { 23 | stack = make([]uintptr, amount) 24 | stack = stack[:runtime.Callers(offset, stack)] 25 | if len(stack) < amount { 26 | return stack, 0 27 | } 28 | return stack, offset + len(stack) 29 | } 30 | ) 31 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | In general, the code posted to the [SmartyStreets github organization](https://github.com/smartystreets) is created to solve specific problems at SmartyStreets that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted. 4 | 5 | Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines: 6 | 7 | - _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a SmartyStreets team member prior to opening a pull request. 8 | - _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **SmartyStreets, LLC**. Code submitted to SmartyStreets projects becomes property of SmartyStreets and must be compatible with the associated license. 9 | - _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set. 10 | - _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out. 11 | - "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc... 12 | - "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc... 13 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 SmartyStreets, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | NOTE: Various optional and subordinate components carry their own licensing 22 | requirements and restrictions. Use of those components is subject to the terms 23 | and conditions outlined the respective license of each component. 24 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/doc.go: -------------------------------------------------------------------------------- 1 | // Package assertions contains the implementations for all assertions which 2 | // are referenced in goconvey's `convey` package 3 | // (github.com/smartystreets/goconvey/convey) and gunit (github.com/smartystreets/gunit) 4 | // for use with the So(...) method. 5 | // They can also be used in traditional Go test functions and even in 6 | // applications. 7 | // 8 | // Many of the assertions lean heavily on work done by Aaron Jacobs in his excellent oglematchers library. 9 | // (https://github.com/jacobsa/oglematchers) 10 | // The ShouldResemble assertion leans heavily on work done by Daniel Jacques in his very helpful go-render library. 11 | // (https://github.com/luci/go-render) 12 | package assertions 13 | 14 | import ( 15 | "fmt" 16 | "runtime" 17 | ) 18 | 19 | // By default we use a no-op serializer. The actual Serializer provides a JSON 20 | // representation of failure results on selected assertions so the goconvey 21 | // web UI can display a convenient diff. 22 | var serializer Serializer = new(noopSerializer) 23 | 24 | // GoConveyMode provides control over JSON serialization of failures. When 25 | // using the assertions in this package from the convey package JSON results 26 | // are very helpful and can be rendered in a DIFF view. In that case, this function 27 | // will be called with a true value to enable the JSON serialization. By default, 28 | // the assertions in this package will not serializer a JSON result, making 29 | // standalone usage more convenient. 30 | func GoConveyMode(yes bool) { 31 | if yes { 32 | serializer = newSerializer() 33 | } else { 34 | serializer = new(noopSerializer) 35 | } 36 | } 37 | 38 | type testingT interface { 39 | Error(args ...interface{}) 40 | } 41 | 42 | type Assertion struct { 43 | t testingT 44 | failed bool 45 | } 46 | 47 | // New swallows the *testing.T struct and prints failed assertions using t.Error. 48 | // Example: assertions.New(t).So(1, should.Equal, 1) 49 | func New(t testingT) *Assertion { 50 | return &Assertion{t: t} 51 | } 52 | 53 | // Failed reports whether any calls to So (on this Assertion instance) have failed. 54 | func (this *Assertion) Failed() bool { 55 | return this.failed 56 | } 57 | 58 | // So calls the standalone So function and additionally, calls t.Error in failure scenarios. 59 | func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool { 60 | ok, result := So(actual, assert, expected...) 61 | if !ok { 62 | this.failed = true 63 | _, file, line, _ := runtime.Caller(1) 64 | this.t.Error(fmt.Sprintf("\n%s:%d\n%s", file, line, result)) 65 | } 66 | return ok 67 | } 68 | 69 | // So is a convenience function (as opposed to an inconvenience function?) 70 | // for running assertions on arbitrary arguments in any context, be it for testing or even 71 | // application logging. It allows you to perform assertion-like behavior (and get nicely 72 | // formatted messages detailing discrepancies) but without the program blowing up or panicking. 73 | // All that is required is to import this package and call `So` with one of the assertions 74 | // exported by this package as the second parameter. 75 | // The first return parameter is a boolean indicating if the assertion was true. The second 76 | // return parameter is the well-formatted message showing why an assertion was incorrect, or 77 | // blank if the assertion was correct. 78 | // 79 | // Example: 80 | // 81 | // if ok, message := So(x, ShouldBeGreaterThan, y); !ok { 82 | // log.Println(message) 83 | // } 84 | // 85 | // For an alternative implementation of So (that provides more flexible return options) 86 | // see the `So` function in the package at github.com/smartystreets/assertions/assert. 87 | func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) { 88 | if result := so(actual, assert, expected...); len(result) == 0 { 89 | return true, result 90 | } else { 91 | return false, result 92 | } 93 | } 94 | 95 | // so is like So, except that it only returns the string message, which is blank if the 96 | // assertion passed. Used to facilitate testing. 97 | func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string { 98 | return assert(actual, expected...) 99 | } 100 | 101 | // assertion is an alias for a function with a signature that the So() 102 | // function can handle. Any future or custom assertions should conform to this 103 | // method signature. The return value should be an empty string if the assertion 104 | // passes and a well-formed failure message if not. 105 | type assertion func(actual interface{}, expected ...interface{}) string 106 | 107 | //////////////////////////////////////////////////////////////////////////// 108 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/equal_method.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import "reflect" 4 | 5 | type equalityMethodSpecification struct { 6 | a interface{} 7 | b interface{} 8 | 9 | aType reflect.Type 10 | bType reflect.Type 11 | 12 | equalMethod reflect.Value 13 | } 14 | 15 | func newEqualityMethodSpecification(a, b interface{}) *equalityMethodSpecification { 16 | return &equalityMethodSpecification{ 17 | a: a, 18 | b: b, 19 | } 20 | } 21 | 22 | func (this *equalityMethodSpecification) IsSatisfied() bool { 23 | if !this.bothAreSameType() { 24 | return false 25 | } 26 | if !this.typeHasEqualMethod() { 27 | return false 28 | } 29 | if !this.equalMethodReceivesSameTypeForComparison() { 30 | return false 31 | } 32 | if !this.equalMethodReturnsBool() { 33 | return false 34 | } 35 | return true 36 | } 37 | 38 | func (this *equalityMethodSpecification) bothAreSameType() bool { 39 | this.aType = reflect.TypeOf(this.a) 40 | if this.aType == nil { 41 | return false 42 | } 43 | if this.aType.Kind() == reflect.Ptr { 44 | this.aType = this.aType.Elem() 45 | } 46 | this.bType = reflect.TypeOf(this.b) 47 | return this.aType == this.bType 48 | } 49 | func (this *equalityMethodSpecification) typeHasEqualMethod() bool { 50 | aInstance := reflect.ValueOf(this.a) 51 | this.equalMethod = aInstance.MethodByName("Equal") 52 | return this.equalMethod != reflect.Value{} 53 | } 54 | 55 | func (this *equalityMethodSpecification) equalMethodReceivesSameTypeForComparison() bool { 56 | signature := this.equalMethod.Type() 57 | return signature.NumIn() == 1 && signature.In(0) == this.aType 58 | } 59 | 60 | func (this *equalityMethodSpecification) equalMethodReturnsBool() bool { 61 | signature := this.equalMethod.Type() 62 | return signature.NumOut() == 1 && signature.Out(0) == reflect.TypeOf(true) 63 | } 64 | 65 | func (this *equalityMethodSpecification) AreEqual() bool { 66 | a := reflect.ValueOf(this.a) 67 | b := reflect.ValueOf(this.b) 68 | return areEqual(a, b) && areEqual(b, a) 69 | } 70 | func areEqual(receiver reflect.Value, argument reflect.Value) bool { 71 | equalMethod := receiver.MethodByName("Equal") 72 | argumentList := []reflect.Value{argument} 73 | result := equalMethod.Call(argumentList) 74 | return result[0].Bool() 75 | } 76 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/filter.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import "fmt" 4 | 5 | const ( 6 | success = "" 7 | needExactValues = "This assertion requires exactly %d comparison values (you provided %d)." 8 | needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)." 9 | needFewerValues = "This assertion allows %d or fewer comparison values (you provided %d)." 10 | ) 11 | 12 | func need(needed int, expected []interface{}) string { 13 | if len(expected) != needed { 14 | return fmt.Sprintf(needExactValues, needed, len(expected)) 15 | } 16 | return success 17 | } 18 | 19 | func atLeast(minimum int, expected []interface{}) string { 20 | if len(expected) < 1 { 21 | return needNonEmptyCollection 22 | } 23 | return success 24 | } 25 | 26 | func atMost(max int, expected []interface{}) string { 27 | if len(expected) > max { 28 | return fmt.Sprintf(needFewerValues, max, len(expected)) 29 | } 30 | return success 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/go-render/LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The Chromium Authors. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers) 2 | 3 | `oglematchers` is a package for the Go programming language containing a set of 4 | matchers, useful in a testing or mocking framework, inspired by and mostly 5 | compatible with [Google Test][googletest] for C++ and 6 | [Google JS Test][google-js-test]. The package is used by the 7 | [ogletest][ogletest] testing framework and [oglemock][oglemock] mocking 8 | framework, which may be more directly useful to you, but can be generically used 9 | elsewhere as well. 10 | 11 | A "matcher" is simply an object with a `Matches` method defining a set of golang 12 | values matched by the matcher, and a `Description` method describing that set. 13 | For example, here are some matchers: 14 | 15 | ```go 16 | // Numbers 17 | Equals(17.13) 18 | LessThan(19) 19 | 20 | // Strings 21 | Equals("taco") 22 | HasSubstr("burrito") 23 | MatchesRegex("t.*o") 24 | 25 | // Combining matchers 26 | AnyOf(LessThan(17), GreaterThan(19)) 27 | ``` 28 | 29 | There are lots more; see [here][reference] for a reference. You can also add 30 | your own simply by implementing the `oglematchers.Matcher` interface. 31 | 32 | 33 | Installation 34 | ------------ 35 | 36 | First, make sure you have installed Go 1.0.2 or newer. See 37 | [here][golang-install] for instructions. 38 | 39 | Use the following command to install `oglematchers` and keep it up to date: 40 | 41 | go get -u github.com/smartystreets/assertions/internal/oglematchers 42 | 43 | 44 | Documentation 45 | ------------- 46 | 47 | See [here][reference] for documentation. Alternatively, you can install the 48 | package and then use `godoc`: 49 | 50 | godoc github.com/smartystreets/assertions/internal/oglematchers 51 | 52 | 53 | [reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers 54 | [golang-install]: http://golang.org/doc/install.html 55 | [googletest]: http://code.google.com/p/googletest/ 56 | [google-js-test]: http://code.google.com/p/google-js-test/ 57 | [ogletest]: http://github.com/smartystreets/assertions/internal/ogletest 58 | [oglemock]: http://github.com/smartystreets/assertions/internal/oglemock 59 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "reflect" 22 | "strings" 23 | ) 24 | 25 | // AnyOf accepts a set of values S and returns a matcher that follows the 26 | // algorithm below when considering a candidate c: 27 | // 28 | // 1. If there exists a value m in S such that m implements the Matcher 29 | // interface and m matches c, return true. 30 | // 31 | // 2. Otherwise, if there exists a value v in S such that v does not implement 32 | // the Matcher interface and the matcher Equals(v) matches c, return true. 33 | // 34 | // 3. Otherwise, if there is a value m in S such that m implements the Matcher 35 | // interface and m returns a fatal error for c, return that fatal error. 36 | // 37 | // 4. Otherwise, return false. 38 | // 39 | // This is akin to a logical OR operation for matchers, with non-matchers x 40 | // being treated as Equals(x). 41 | func AnyOf(vals ...interface{}) Matcher { 42 | // Get ahold of a type variable for the Matcher interface. 43 | var dummy *Matcher 44 | matcherType := reflect.TypeOf(dummy).Elem() 45 | 46 | // Create a matcher for each value, or use the value itself if it's already a 47 | // matcher. 48 | wrapped := make([]Matcher, len(vals)) 49 | for i, v := range vals { 50 | t := reflect.TypeOf(v) 51 | if t != nil && t.Implements(matcherType) { 52 | wrapped[i] = v.(Matcher) 53 | } else { 54 | wrapped[i] = Equals(v) 55 | } 56 | } 57 | 58 | return &anyOfMatcher{wrapped} 59 | } 60 | 61 | type anyOfMatcher struct { 62 | wrapped []Matcher 63 | } 64 | 65 | func (m *anyOfMatcher) Description() string { 66 | wrappedDescs := make([]string, len(m.wrapped)) 67 | for i, matcher := range m.wrapped { 68 | wrappedDescs[i] = matcher.Description() 69 | } 70 | 71 | return fmt.Sprintf("or(%s)", strings.Join(wrappedDescs, ", ")) 72 | } 73 | 74 | func (m *anyOfMatcher) Matches(c interface{}) (err error) { 75 | err = errors.New("") 76 | 77 | // Try each matcher in turn. 78 | for _, matcher := range m.wrapped { 79 | wrappedErr := matcher.Matches(c) 80 | 81 | // Return immediately if there's a match. 82 | if wrappedErr == nil { 83 | err = nil 84 | return 85 | } 86 | 87 | // Note the fatal error, if any. 88 | if _, isFatal := wrappedErr.(*FatalError); isFatal { 89 | err = wrappedErr 90 | } 91 | } 92 | 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // Return a matcher that matches arrays slices with at least one element that 24 | // matches the supplied argument. If the argument x is not itself a Matcher, 25 | // this is equivalent to Contains(Equals(x)). 26 | func Contains(x interface{}) Matcher { 27 | var result containsMatcher 28 | var ok bool 29 | 30 | if result.elementMatcher, ok = x.(Matcher); !ok { 31 | result.elementMatcher = DeepEquals(x) 32 | } 33 | 34 | return &result 35 | } 36 | 37 | type containsMatcher struct { 38 | elementMatcher Matcher 39 | } 40 | 41 | func (m *containsMatcher) Description() string { 42 | return fmt.Sprintf("contains: %s", m.elementMatcher.Description()) 43 | } 44 | 45 | func (m *containsMatcher) Matches(candidate interface{}) error { 46 | // The candidate must be a slice or an array. 47 | v := reflect.ValueOf(candidate) 48 | if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { 49 | return NewFatalError("which is not a slice or array") 50 | } 51 | 52 | // Check each element. 53 | for i := 0; i < v.Len(); i++ { 54 | elem := v.Index(i) 55 | if matchErr := m.elementMatcher.Matches(elem.Interface()); matchErr == nil { 56 | return nil 57 | } 58 | } 59 | 60 | return fmt.Errorf("") 61 | } 62 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "bytes" 20 | "errors" 21 | "fmt" 22 | "reflect" 23 | ) 24 | 25 | var byteSliceType reflect.Type = reflect.TypeOf([]byte{}) 26 | 27 | // DeepEquals returns a matcher that matches based on 'deep equality', as 28 | // defined by the reflect package. This matcher requires that values have 29 | // identical types to x. 30 | func DeepEquals(x interface{}) Matcher { 31 | return &deepEqualsMatcher{x} 32 | } 33 | 34 | type deepEqualsMatcher struct { 35 | x interface{} 36 | } 37 | 38 | func (m *deepEqualsMatcher) Description() string { 39 | xDesc := fmt.Sprintf("%v", m.x) 40 | xValue := reflect.ValueOf(m.x) 41 | 42 | // Special case: fmt.Sprintf presents nil slices as "[]", but 43 | // reflect.DeepEqual makes a distinction between nil and empty slices. Make 44 | // this less confusing. 45 | if xValue.Kind() == reflect.Slice && xValue.IsNil() { 46 | xDesc = "" 47 | } 48 | 49 | return fmt.Sprintf("deep equals: %s", xDesc) 50 | } 51 | 52 | func (m *deepEqualsMatcher) Matches(c interface{}) error { 53 | // Make sure the types match. 54 | ct := reflect.TypeOf(c) 55 | xt := reflect.TypeOf(m.x) 56 | 57 | if ct != xt { 58 | return NewFatalError(fmt.Sprintf("which is of type %v", ct)) 59 | } 60 | 61 | // Special case: handle byte slices more efficiently. 62 | cValue := reflect.ValueOf(c) 63 | xValue := reflect.ValueOf(m.x) 64 | 65 | if ct == byteSliceType && !cValue.IsNil() && !xValue.IsNil() { 66 | xBytes := m.x.([]byte) 67 | cBytes := c.([]byte) 68 | 69 | if bytes.Equal(cBytes, xBytes) { 70 | return nil 71 | } 72 | 73 | return errors.New("") 74 | } 75 | 76 | // Defer to the reflect package. 77 | if reflect.DeepEqual(m.x, c) { 78 | return nil 79 | } 80 | 81 | // Special case: if the comparison failed because c is the nil slice, given 82 | // an indication of this (since its value is printed as "[]"). 83 | if cValue.Kind() == reflect.Slice && cValue.IsNil() { 84 | return errors.New("which is nil") 85 | } 86 | 87 | return errors.New("") 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // GreaterOrEqual returns a matcher that matches integer, floating point, or 24 | // strings values v such that v >= x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // GreaterOrEqual will panic. 30 | func GreaterOrEqual(x interface{}) Matcher { 31 | desc := fmt.Sprintf("greater than or equal to %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("greater than or equal to \"%s\"", x) 36 | } 37 | 38 | return transformDescription(Not(LessThan(x)), desc) 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // GreaterThan returns a matcher that matches integer, floating point, or 24 | // strings values v such that v > x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // GreaterThan will panic. 30 | func GreaterThan(x interface{}) Matcher { 31 | desc := fmt.Sprintf("greater than %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("greater than \"%s\"", x) 36 | } 37 | 38 | return transformDescription(Not(LessOrEqual(x)), desc) 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // LessOrEqual returns a matcher that matches integer, floating point, or 24 | // strings values v such that v <= x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // LessOrEqual will panic. 30 | func LessOrEqual(x interface{}) Matcher { 31 | desc := fmt.Sprintf("less than or equal to %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("less than or equal to \"%s\"", x) 36 | } 37 | 38 | // Put LessThan last so that its error messages will be used in the event of 39 | // failure. 40 | return transformDescription(AnyOf(Equals(x), LessThan(x)), desc) 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "math" 22 | "reflect" 23 | ) 24 | 25 | // LessThan returns a matcher that matches integer, floating point, or strings 26 | // values v such that v < x. Comparison is not defined between numeric and 27 | // string types, but is defined between all integer and floating point types. 28 | // 29 | // x must itself be an integer, floating point, or string type; otherwise, 30 | // LessThan will panic. 31 | func LessThan(x interface{}) Matcher { 32 | v := reflect.ValueOf(x) 33 | kind := v.Kind() 34 | 35 | switch { 36 | case isInteger(v): 37 | case isFloat(v): 38 | case kind == reflect.String: 39 | 40 | default: 41 | panic(fmt.Sprintf("LessThan: unexpected kind %v", kind)) 42 | } 43 | 44 | return &lessThanMatcher{v} 45 | } 46 | 47 | type lessThanMatcher struct { 48 | limit reflect.Value 49 | } 50 | 51 | func (m *lessThanMatcher) Description() string { 52 | // Special case: make it clear that strings are strings. 53 | if m.limit.Kind() == reflect.String { 54 | return fmt.Sprintf("less than \"%s\"", m.limit.String()) 55 | } 56 | 57 | return fmt.Sprintf("less than %v", m.limit.Interface()) 58 | } 59 | 60 | func compareIntegers(v1, v2 reflect.Value) (err error) { 61 | err = errors.New("") 62 | 63 | switch { 64 | case isSignedInteger(v1) && isSignedInteger(v2): 65 | if v1.Int() < v2.Int() { 66 | err = nil 67 | } 68 | return 69 | 70 | case isSignedInteger(v1) && isUnsignedInteger(v2): 71 | if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() { 72 | err = nil 73 | } 74 | return 75 | 76 | case isUnsignedInteger(v1) && isSignedInteger(v2): 77 | if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() { 78 | err = nil 79 | } 80 | return 81 | 82 | case isUnsignedInteger(v1) && isUnsignedInteger(v2): 83 | if v1.Uint() < v2.Uint() { 84 | err = nil 85 | } 86 | return 87 | } 88 | 89 | panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2)) 90 | } 91 | 92 | func getFloat(v reflect.Value) float64 { 93 | switch { 94 | case isSignedInteger(v): 95 | return float64(v.Int()) 96 | 97 | case isUnsignedInteger(v): 98 | return float64(v.Uint()) 99 | 100 | case isFloat(v): 101 | return v.Float() 102 | } 103 | 104 | panic(fmt.Sprintf("getFloat: %v", v)) 105 | } 106 | 107 | func (m *lessThanMatcher) Matches(c interface{}) (err error) { 108 | v1 := reflect.ValueOf(c) 109 | v2 := m.limit 110 | 111 | err = errors.New("") 112 | 113 | // Handle strings as a special case. 114 | if v1.Kind() == reflect.String && v2.Kind() == reflect.String { 115 | if v1.String() < v2.String() { 116 | err = nil 117 | } 118 | return 119 | } 120 | 121 | // If we get here, we require that we are dealing with integers or floats. 122 | v1Legal := isInteger(v1) || isFloat(v1) 123 | v2Legal := isInteger(v2) || isFloat(v2) 124 | if !v1Legal || !v2Legal { 125 | err = NewFatalError("which is not comparable") 126 | return 127 | } 128 | 129 | // Handle the various comparison cases. 130 | switch { 131 | // Both integers 132 | case isInteger(v1) && isInteger(v2): 133 | return compareIntegers(v1, v2) 134 | 135 | // At least one float32 136 | case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32: 137 | if float32(getFloat(v1)) < float32(getFloat(v2)) { 138 | err = nil 139 | } 140 | return 141 | 142 | // At least one float64 143 | case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64: 144 | if getFloat(v1) < getFloat(v2) { 145 | err = nil 146 | } 147 | return 148 | } 149 | 150 | // We shouldn't get here. 151 | panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2)) 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Package oglematchers provides a set of matchers useful in a testing or 17 | // mocking framework. These matchers are inspired by and mostly compatible with 18 | // Google Test for C++ and Google JS Test. 19 | // 20 | // This package is used by github.com/smartystreets/assertions/internal/ogletest and 21 | // github.com/smartystreets/assertions/internal/oglemock, which may be more directly useful if you're not 22 | // writing your own testing package or defining your own matchers. 23 | package oglematchers 24 | 25 | // A Matcher is some predicate implicitly defining a set of values that it 26 | // matches. For example, GreaterThan(17) matches all numeric values greater 27 | // than 17, and HasSubstr("taco") matches all strings with the substring 28 | // "taco". 29 | // 30 | // Matchers are typically exposed to tests via constructor functions like 31 | // HasSubstr. In order to implement such a function you can either define your 32 | // own matcher type or use NewMatcher. 33 | type Matcher interface { 34 | // Check whether the supplied value belongs to the the set defined by the 35 | // matcher. Return a non-nil error if and only if it does not. 36 | // 37 | // The error describes why the value doesn't match. The error text is a 38 | // relative clause that is suitable for being placed after the value. For 39 | // example, a predicate that matches strings with a particular substring may, 40 | // when presented with a numerical value, return the following error text: 41 | // 42 | // "which is not a string" 43 | // 44 | // Then the failure message may look like: 45 | // 46 | // Expected: has substring "taco" 47 | // Actual: 17, which is not a string 48 | // 49 | // If the error is self-apparent based on the description of the matcher, the 50 | // error text may be empty (but the error still non-nil). For example: 51 | // 52 | // Expected: 17 53 | // Actual: 19 54 | // 55 | // If you are implementing a new matcher, see also the documentation on 56 | // FatalError. 57 | Matches(candidate interface{}) error 58 | 59 | // Description returns a string describing the property that values matching 60 | // this matcher have, as a verb phrase where the subject is the value. For 61 | // example, "is greather than 17" or "has substring "taco"". 62 | Description() string 63 | } 64 | 65 | // FatalError is an implementation of the error interface that may be returned 66 | // from matchers, indicating the error should be propagated. Returning a 67 | // *FatalError indicates that the matcher doesn't process values of the 68 | // supplied type, or otherwise doesn't know how to handle the value. 69 | // 70 | // For example, if GreaterThan(17) returned false for the value "taco" without 71 | // a fatal error, then Not(GreaterThan(17)) would return true. This is 72 | // technically correct, but is surprising and may mask failures where the wrong 73 | // sort of matcher is accidentally used. Instead, GreaterThan(17) can return a 74 | // fatal error, which will be propagated by Not(). 75 | type FatalError struct { 76 | errorText string 77 | } 78 | 79 | // NewFatalError creates a FatalError struct with the supplied error text. 80 | func NewFatalError(s string) *FatalError { 81 | return &FatalError{s} 82 | } 83 | 84 | func (e *FatalError) Error() string { 85 | return e.errorText 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | ) 22 | 23 | // Not returns a matcher that inverts the set of values matched by the wrapped 24 | // matcher. It does not transform the result for values for which the wrapped 25 | // matcher returns a fatal error. 26 | func Not(m Matcher) Matcher { 27 | return ¬Matcher{m} 28 | } 29 | 30 | type notMatcher struct { 31 | wrapped Matcher 32 | } 33 | 34 | func (m *notMatcher) Matches(c interface{}) (err error) { 35 | err = m.wrapped.Matches(c) 36 | 37 | // Did the wrapped matcher say yes? 38 | if err == nil { 39 | return errors.New("") 40 | } 41 | 42 | // Did the wrapped matcher return a fatal error? 43 | if _, isFatal := err.(*FatalError); isFatal { 44 | return err 45 | } 46 | 47 | // The wrapped matcher returned a non-fatal error. 48 | return nil 49 | } 50 | 51 | func (m *notMatcher) Description() string { 52 | return fmt.Sprintf("not(%s)", m.wrapped.Description()) 53 | } 54 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | // transformDescription returns a matcher that is equivalent to the supplied 19 | // one, except that it has the supplied description instead of the one attached 20 | // to the existing matcher. 21 | func transformDescription(m Matcher, newDesc string) Matcher { 22 | return &transformDescriptionMatcher{newDesc, m} 23 | } 24 | 25 | type transformDescriptionMatcher struct { 26 | desc string 27 | wrappedMatcher Matcher 28 | } 29 | 30 | func (m *transformDescriptionMatcher) Description() string { 31 | return m.desc 32 | } 33 | 34 | func (m *transformDescriptionMatcher) Matches(c interface{}) error { 35 | return m.wrappedMatcher.Matches(c) 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/messages.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | const ( // equality 4 | shouldHaveBeenEqual = "Expected: '%v'\nActual: '%v'\n(Should be equal)" 5 | shouldNotHaveBeenEqual = "Expected '%v'\nto NOT equal '%v'\n(but it did)!" 6 | shouldHaveBeenEqualTypeMismatch = "Expected: '%v' (%T)\nActual: '%v' (%T)\n(Should be equal, type mismatch)" 7 | shouldHaveBeenAlmostEqual = "Expected '%v' to almost equal '%v' (but it didn't)!" 8 | shouldHaveNotBeenAlmostEqual = "Expected '%v' to NOT almost equal '%v' (but it did)!" 9 | shouldHaveResembled = "Expected: '%s'\nActual: '%s'\n(Should resemble)!" 10 | shouldNotHaveResembled = "Expected '%#v'\nto NOT resemble '%#v'\n(but it did)!" 11 | shouldBePointers = "Both arguments should be pointers " 12 | shouldHaveBeenNonNilPointer = shouldBePointers + "(the %s was %s)!" 13 | shouldHavePointedTo = "Expected '%+v' (address: '%v') and '%+v' (address: '%v') to be the same address (but their weren't)!" 14 | shouldNotHavePointedTo = "Expected '%+v' and '%+v' to be different references (but they matched: '%v')!" 15 | shouldHaveBeenNil = "Expected: nil\nActual: '%v'" 16 | shouldNotHaveBeenNil = "Expected '%+v' to NOT be nil (but it was)!" 17 | shouldHaveBeenTrue = "Expected: true\nActual: %v" 18 | shouldHaveBeenFalse = "Expected: false\nActual: %v" 19 | shouldHaveBeenZeroValue = "'%+v' should have been the zero value" //"Expected: (zero value)\nActual: %v" 20 | ) 21 | 22 | const ( // quantity comparisons 23 | shouldHaveBeenGreater = "Expected '%v' to be greater than '%v' (but it wasn't)!" 24 | shouldHaveBeenGreaterOrEqual = "Expected '%v' to be greater than or equal to '%v' (but it wasn't)!" 25 | shouldHaveBeenLess = "Expected '%v' to be less than '%v' (but it wasn't)!" 26 | shouldHaveBeenLessOrEqual = "Expected '%v' to be less than or equal to '%v' (but it wasn't)!" 27 | shouldHaveBeenBetween = "Expected '%v' to be between '%v' and '%v' (but it wasn't)!" 28 | shouldNotHaveBeenBetween = "Expected '%v' NOT to be between '%v' and '%v' (but it was)!" 29 | shouldHaveDifferentUpperAndLower = "The lower and upper bounds must be different values (they were both '%v')." 30 | shouldHaveBeenBetweenOrEqual = "Expected '%v' to be between '%v' and '%v' or equal to one of them (but it wasn't)!" 31 | shouldNotHaveBeenBetweenOrEqual = "Expected '%v' NOT to be between '%v' and '%v' or equal to one of them (but it was)!" 32 | ) 33 | 34 | const ( // collections 35 | shouldHaveContained = "Expected the container (%v) to contain: '%v' (but it didn't)!" 36 | shouldNotHaveContained = "Expected the container (%v) NOT to contain: '%v' (but it did)!" 37 | shouldHaveContainedKey = "Expected the %v to contain the key: %v (but it didn't)!" 38 | shouldNotHaveContainedKey = "Expected the %v NOT to contain the key: %v (but it did)!" 39 | shouldHaveBeenIn = "Expected '%v' to be in the container (%v), but it wasn't!" 40 | shouldNotHaveBeenIn = "Expected '%v' NOT to be in the container (%v), but it was!" 41 | shouldHaveBeenAValidCollection = "You must provide a valid container (was %v)!" 42 | shouldHaveBeenAValidMap = "You must provide a valid map type (was %v)!" 43 | shouldHaveBeenEmpty = "Expected %+v to be empty (but it wasn't)!" 44 | shouldNotHaveBeenEmpty = "Expected %+v to NOT be empty (but it was)!" 45 | shouldHaveBeenAValidInteger = "You must provide a valid integer (was %v)!" 46 | shouldHaveBeenAValidLength = "You must provide a valid positive integer (was %v)!" 47 | shouldHaveHadLength = "Expected %+v (length: %v) to have length equal to '%v', but it wasn't!" 48 | ) 49 | 50 | const ( // strings 51 | shouldHaveStartedWith = "Expected '%v'\nto start with '%v'\n(but it didn't)!" 52 | shouldNotHaveStartedWith = "Expected '%v'\nNOT to start with '%v'\n(but it did)!" 53 | shouldHaveEndedWith = "Expected '%v'\nto end with '%v'\n(but it didn't)!" 54 | shouldNotHaveEndedWith = "Expected '%v'\nNOT to end with '%v'\n(but it did)!" 55 | shouldAllBeStrings = "All arguments to this assertion must be strings (you provided: %v)." 56 | shouldBothBeStrings = "Both arguments to this assertion must be strings (you provided %v and %v)." 57 | shouldBeString = "The argument to this assertion must be a string (you provided %v)." 58 | shouldHaveContainedSubstring = "Expected '%s' to contain substring '%s' (but it didn't)!" 59 | shouldNotHaveContainedSubstring = "Expected '%s' NOT to contain substring '%s' (but it did)!" 60 | shouldHaveBeenBlank = "Expected '%s' to be blank (but it wasn't)!" 61 | shouldNotHaveBeenBlank = "Expected value to NOT be blank (but it was)!" 62 | ) 63 | 64 | const ( // panics 65 | shouldUseVoidNiladicFunction = "You must provide a void, niladic function as the first argument!" 66 | shouldHavePanickedWith = "Expected func() to panic with '%v' (but it panicked with '%v')!" 67 | shouldHavePanicked = "Expected func() to panic (but it didn't)!" 68 | shouldNotHavePanicked = "Expected func() NOT to panic (error: '%+v')!" 69 | shouldNotHavePanickedWith = "Expected func() NOT to panic with '%v' (but it did)!" 70 | ) 71 | 72 | const ( // type checking 73 | shouldHaveBeenA = "Expected '%v' to be: '%v' (but was: '%v')!" 74 | shouldNotHaveBeenA = "Expected '%v' to NOT be: '%v' (but it was)!" 75 | 76 | shouldHaveImplemented = "Expected: '%v interface support'\nActual: '%v' does not implement the interface!" 77 | shouldNotHaveImplemented = "Expected '%v'\nto NOT implement '%v'\n(but it did)!" 78 | shouldCompareWithInterfacePointer = "The expected value must be a pointer to an interface type (eg. *fmt.Stringer)" 79 | shouldNotBeNilActual = "The actual value was 'nil' and should be a value or a pointer to a value!" 80 | 81 | shouldBeError = "Expected an error value (but was '%v' instead)!" 82 | shouldBeErrorInvalidComparisonValue = "The final argument to this assertion must be a string or an error value (you provided: '%v')." 83 | ) 84 | 85 | const ( // time comparisons 86 | shouldUseTimes = "You must provide time instances as arguments to this assertion." 87 | shouldUseTimeSlice = "You must provide a slice of time instances as the first argument to this assertion." 88 | shouldUseDurationAndTime = "You must provide a duration and a time as arguments to this assertion." 89 | shouldHaveHappenedBefore = "Expected '%v' to happen before '%v' (it happened '%v' after)!" 90 | shouldHaveHappenedAfter = "Expected '%v' to happen after '%v' (it happened '%v' before)!" 91 | shouldHaveHappenedBetween = "Expected '%v' to happen between '%v' and '%v' (it happened '%v' outside threshold)!" 92 | shouldNotHaveHappenedOnOrBetween = "Expected '%v' to NOT happen on or between '%v' and '%v' (but it did)!" 93 | 94 | // format params: incorrect-index, previous-index, previous-time, incorrect-index, incorrect-time 95 | shouldHaveBeenChronological = "The 'Time' at index [%d] should have happened after the previous one (but it didn't!):\n [%d]: %s\n [%d]: %s (see, it happened before!)" 96 | ) 97 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/panic.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import "fmt" 4 | 5 | // ShouldPanic receives a void, niladic function and expects to recover a panic. 6 | func ShouldPanic(actual interface{}, expected ...interface{}) (message string) { 7 | if fail := need(0, expected); fail != success { 8 | return fail 9 | } 10 | 11 | action, _ := actual.(func()) 12 | 13 | if action == nil { 14 | message = shouldUseVoidNiladicFunction 15 | return 16 | } 17 | 18 | defer func() { 19 | recovered := recover() 20 | if recovered == nil { 21 | message = shouldHavePanicked 22 | } else { 23 | message = success 24 | } 25 | }() 26 | action() 27 | 28 | return 29 | } 30 | 31 | // ShouldNotPanic receives a void, niladic function and expects to execute the function without any panic. 32 | func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) { 33 | if fail := need(0, expected); fail != success { 34 | return fail 35 | } 36 | 37 | action, _ := actual.(func()) 38 | 39 | if action == nil { 40 | message = shouldUseVoidNiladicFunction 41 | return 42 | } 43 | 44 | defer func() { 45 | recovered := recover() 46 | if recovered != nil { 47 | message = fmt.Sprintf(shouldNotHavePanicked, recovered) 48 | } else { 49 | message = success 50 | } 51 | }() 52 | action() 53 | 54 | return 55 | } 56 | 57 | // ShouldPanicWith receives a void, niladic function and expects to recover a panic with the second argument as the content. 58 | func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) { 59 | if fail := need(1, expected); fail != success { 60 | return fail 61 | } 62 | 63 | action, _ := actual.(func()) 64 | 65 | if action == nil { 66 | message = shouldUseVoidNiladicFunction 67 | return 68 | } 69 | 70 | defer func() { 71 | recovered := recover() 72 | if recovered == nil { 73 | message = shouldHavePanicked 74 | } else { 75 | if equal := ShouldEqual(recovered, expected[0]); equal != success { 76 | message = serializer.serialize(expected[0], recovered, fmt.Sprintf(shouldHavePanickedWith, expected[0], recovered)) 77 | } else { 78 | message = success 79 | } 80 | } 81 | }() 82 | action() 83 | 84 | return 85 | } 86 | 87 | // ShouldNotPanicWith receives a void, niladic function and expects to recover a panic whose content differs from the second argument. 88 | func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) { 89 | if fail := need(1, expected); fail != success { 90 | return fail 91 | } 92 | 93 | action, _ := actual.(func()) 94 | 95 | if action == nil { 96 | message = shouldUseVoidNiladicFunction 97 | return 98 | } 99 | 100 | defer func() { 101 | recovered := recover() 102 | if recovered == nil { 103 | message = success 104 | } else { 105 | if equal := ShouldEqual(recovered, expected[0]); equal == success { 106 | message = fmt.Sprintf(shouldNotHavePanickedWith, expected[0]) 107 | } else { 108 | message = success 109 | } 110 | } 111 | }() 112 | action() 113 | 114 | return 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/quantity.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/smartystreets/assertions/internal/oglematchers" 7 | ) 8 | 9 | // ShouldBeGreaterThan receives exactly two parameters and ensures that the first is greater than the second. 10 | func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string { 11 | if fail := need(1, expected); fail != success { 12 | return fail 13 | } 14 | 15 | if matchError := oglematchers.GreaterThan(expected[0]).Matches(actual); matchError != nil { 16 | return fmt.Sprintf(shouldHaveBeenGreater, actual, expected[0]) 17 | } 18 | return success 19 | } 20 | 21 | // ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that the first is greater than or equal to the second. 22 | func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string { 23 | if fail := need(1, expected); fail != success { 24 | return fail 25 | } else if matchError := oglematchers.GreaterOrEqual(expected[0]).Matches(actual); matchError != nil { 26 | return fmt.Sprintf(shouldHaveBeenGreaterOrEqual, actual, expected[0]) 27 | } 28 | return success 29 | } 30 | 31 | // ShouldBeLessThan receives exactly two parameters and ensures that the first is less than the second. 32 | func ShouldBeLessThan(actual interface{}, expected ...interface{}) string { 33 | if fail := need(1, expected); fail != success { 34 | return fail 35 | } else if matchError := oglematchers.LessThan(expected[0]).Matches(actual); matchError != nil { 36 | return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0]) 37 | } 38 | return success 39 | } 40 | 41 | // ShouldBeLessThan receives exactly two parameters and ensures that the first is less than or equal to the second. 42 | func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string { 43 | if fail := need(1, expected); fail != success { 44 | return fail 45 | } else if matchError := oglematchers.LessOrEqual(expected[0]).Matches(actual); matchError != nil { 46 | return fmt.Sprintf(shouldHaveBeenLessOrEqual, actual, expected[0]) 47 | } 48 | return success 49 | } 50 | 51 | // ShouldBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound. 52 | // It ensures that the actual value is between both bounds (but not equal to either of them). 53 | func ShouldBeBetween(actual interface{}, expected ...interface{}) string { 54 | if fail := need(2, expected); fail != success { 55 | return fail 56 | } 57 | lower, upper, fail := deriveBounds(expected) 58 | 59 | if fail != success { 60 | return fail 61 | } else if !isBetween(actual, lower, upper) { 62 | return fmt.Sprintf(shouldHaveBeenBetween, actual, lower, upper) 63 | } 64 | return success 65 | } 66 | 67 | // ShouldNotBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound. 68 | // It ensures that the actual value is NOT between both bounds. 69 | func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string { 70 | if fail := need(2, expected); fail != success { 71 | return fail 72 | } 73 | lower, upper, fail := deriveBounds(expected) 74 | 75 | if fail != success { 76 | return fail 77 | } else if isBetween(actual, lower, upper) { 78 | return fmt.Sprintf(shouldNotHaveBeenBetween, actual, lower, upper) 79 | } 80 | return success 81 | } 82 | func deriveBounds(values []interface{}) (lower interface{}, upper interface{}, fail string) { 83 | lower = values[0] 84 | upper = values[1] 85 | 86 | if ShouldNotEqual(lower, upper) != success { 87 | return nil, nil, fmt.Sprintf(shouldHaveDifferentUpperAndLower, lower) 88 | } else if ShouldBeLessThan(lower, upper) != success { 89 | lower, upper = upper, lower 90 | } 91 | return lower, upper, success 92 | } 93 | func isBetween(value, lower, upper interface{}) bool { 94 | if ShouldBeGreaterThan(value, lower) != success { 95 | return false 96 | } else if ShouldBeLessThan(value, upper) != success { 97 | return false 98 | } 99 | return true 100 | } 101 | 102 | // ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound. 103 | // It ensures that the actual value is between both bounds or equal to one of them. 104 | func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string { 105 | if fail := need(2, expected); fail != success { 106 | return fail 107 | } 108 | lower, upper, fail := deriveBounds(expected) 109 | 110 | if fail != success { 111 | return fail 112 | } else if !isBetweenOrEqual(actual, lower, upper) { 113 | return fmt.Sprintf(shouldHaveBeenBetweenOrEqual, actual, lower, upper) 114 | } 115 | return success 116 | } 117 | 118 | // ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound. 119 | // It ensures that the actual value is nopt between the bounds nor equal to either of them. 120 | func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string { 121 | if fail := need(2, expected); fail != success { 122 | return fail 123 | } 124 | lower, upper, fail := deriveBounds(expected) 125 | 126 | if fail != success { 127 | return fail 128 | } else if isBetweenOrEqual(actual, lower, upper) { 129 | return fmt.Sprintf(shouldNotHaveBeenBetweenOrEqual, actual, lower, upper) 130 | } 131 | return success 132 | } 133 | 134 | func isBetweenOrEqual(value, lower, upper interface{}) bool { 135 | if ShouldBeGreaterThanOrEqualTo(value, lower) != success { 136 | return false 137 | } else if ShouldBeLessThanOrEqualTo(value, upper) != success { 138 | return false 139 | } 140 | return true 141 | } 142 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/serializer.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/smartystreets/assertions/internal/go-render/render" 8 | ) 9 | 10 | type Serializer interface { 11 | serialize(expected, actual interface{}, message string) string 12 | serializeDetailed(expected, actual interface{}, message string) string 13 | } 14 | 15 | type failureSerializer struct{} 16 | 17 | func (self *failureSerializer) serializeDetailed(expected, actual interface{}, message string) string { 18 | view := FailureView{ 19 | Message: message, 20 | Expected: render.Render(expected), 21 | Actual: render.Render(actual), 22 | } 23 | serialized, _ := json.Marshal(view) 24 | return string(serialized) 25 | } 26 | 27 | func (self *failureSerializer) serialize(expected, actual interface{}, message string) string { 28 | view := FailureView{ 29 | Message: message, 30 | Expected: fmt.Sprintf("%+v", expected), 31 | Actual: fmt.Sprintf("%+v", actual), 32 | } 33 | serialized, _ := json.Marshal(view) 34 | return string(serialized) 35 | } 36 | 37 | func newSerializer() *failureSerializer { 38 | return &failureSerializer{} 39 | } 40 | 41 | /////////////////////////////////////////////////////////////////////////////// 42 | 43 | // This struct is also declared in github.com/smartystreets/goconvey/convey/reporting. 44 | // The json struct tags should be equal in both declarations. 45 | type FailureView struct { 46 | Message string `json:"Message"` 47 | Expected string `json:"Expected"` 48 | Actual string `json:"Actual"` 49 | } 50 | 51 | /////////////////////////////////////////////////////// 52 | 53 | // noopSerializer just gives back the original message. This is useful when we are using 54 | // the assertions from a context other than the GoConvey Web UI, that requires the JSON 55 | // structure provided by the failureSerializer. 56 | type noopSerializer struct{} 57 | 58 | func (self *noopSerializer) serialize(expected, actual interface{}, message string) string { 59 | return message 60 | } 61 | func (self *noopSerializer) serializeDetailed(expected, actual interface{}, message string) string { 62 | return message 63 | } 64 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/time.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the first happens before the second. 9 | func ShouldHappenBefore(actual interface{}, expected ...interface{}) string { 10 | if fail := need(1, expected); fail != success { 11 | return fail 12 | } 13 | actualTime, firstOk := actual.(time.Time) 14 | expectedTime, secondOk := expected[0].(time.Time) 15 | 16 | if !firstOk || !secondOk { 17 | return shouldUseTimes 18 | } 19 | 20 | if !actualTime.Before(expectedTime) { 21 | return fmt.Sprintf(shouldHaveHappenedBefore, actualTime, expectedTime, actualTime.Sub(expectedTime)) 22 | } 23 | 24 | return success 25 | } 26 | 27 | // ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that the first happens on or before the second. 28 | func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string { 29 | if fail := need(1, expected); fail != success { 30 | return fail 31 | } 32 | actualTime, firstOk := actual.(time.Time) 33 | expectedTime, secondOk := expected[0].(time.Time) 34 | 35 | if !firstOk || !secondOk { 36 | return shouldUseTimes 37 | } 38 | 39 | if actualTime.Equal(expectedTime) { 40 | return success 41 | } 42 | return ShouldHappenBefore(actualTime, expectedTime) 43 | } 44 | 45 | // ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the first happens after the second. 46 | func ShouldHappenAfter(actual interface{}, expected ...interface{}) string { 47 | if fail := need(1, expected); fail != success { 48 | return fail 49 | } 50 | actualTime, firstOk := actual.(time.Time) 51 | expectedTime, secondOk := expected[0].(time.Time) 52 | 53 | if !firstOk || !secondOk { 54 | return shouldUseTimes 55 | } 56 | if !actualTime.After(expectedTime) { 57 | return fmt.Sprintf(shouldHaveHappenedAfter, actualTime, expectedTime, expectedTime.Sub(actualTime)) 58 | } 59 | return success 60 | } 61 | 62 | // ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that the first happens on or after the second. 63 | func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string { 64 | if fail := need(1, expected); fail != success { 65 | return fail 66 | } 67 | actualTime, firstOk := actual.(time.Time) 68 | expectedTime, secondOk := expected[0].(time.Time) 69 | 70 | if !firstOk || !secondOk { 71 | return shouldUseTimes 72 | } 73 | if actualTime.Equal(expectedTime) { 74 | return success 75 | } 76 | return ShouldHappenAfter(actualTime, expectedTime) 77 | } 78 | 79 | // ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the first happens between (not on) the second and third. 80 | func ShouldHappenBetween(actual interface{}, expected ...interface{}) string { 81 | if fail := need(2, expected); fail != success { 82 | return fail 83 | } 84 | actualTime, firstOk := actual.(time.Time) 85 | min, secondOk := expected[0].(time.Time) 86 | max, thirdOk := expected[1].(time.Time) 87 | 88 | if !firstOk || !secondOk || !thirdOk { 89 | return shouldUseTimes 90 | } 91 | 92 | if !actualTime.After(min) { 93 | return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, min.Sub(actualTime)) 94 | } 95 | if !actualTime.Before(max) { 96 | return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, actualTime.Sub(max)) 97 | } 98 | return success 99 | } 100 | 101 | // ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first happens between or on the second and third. 102 | func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string { 103 | if fail := need(2, expected); fail != success { 104 | return fail 105 | } 106 | actualTime, firstOk := actual.(time.Time) 107 | min, secondOk := expected[0].(time.Time) 108 | max, thirdOk := expected[1].(time.Time) 109 | 110 | if !firstOk || !secondOk || !thirdOk { 111 | return shouldUseTimes 112 | } 113 | if actualTime.Equal(min) || actualTime.Equal(max) { 114 | return success 115 | } 116 | return ShouldHappenBetween(actualTime, min, max) 117 | } 118 | 119 | // ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first 120 | // does NOT happen between or on the second or third. 121 | func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string { 122 | if fail := need(2, expected); fail != success { 123 | return fail 124 | } 125 | actualTime, firstOk := actual.(time.Time) 126 | min, secondOk := expected[0].(time.Time) 127 | max, thirdOk := expected[1].(time.Time) 128 | 129 | if !firstOk || !secondOk || !thirdOk { 130 | return shouldUseTimes 131 | } 132 | if actualTime.Equal(min) || actualTime.Equal(max) { 133 | return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max) 134 | } 135 | if actualTime.After(min) && actualTime.Before(max) { 136 | return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max) 137 | } 138 | return success 139 | } 140 | 141 | // ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments) 142 | // and asserts that the first time.Time happens within or on the duration specified relative to 143 | // the other time.Time. 144 | func ShouldHappenWithin(actual interface{}, expected ...interface{}) string { 145 | if fail := need(2, expected); fail != success { 146 | return fail 147 | } 148 | actualTime, firstOk := actual.(time.Time) 149 | tolerance, secondOk := expected[0].(time.Duration) 150 | threshold, thirdOk := expected[1].(time.Time) 151 | 152 | if !firstOk || !secondOk || !thirdOk { 153 | return shouldUseDurationAndTime 154 | } 155 | 156 | min := threshold.Add(-tolerance) 157 | max := threshold.Add(tolerance) 158 | return ShouldHappenOnOrBetween(actualTime, min, max) 159 | } 160 | 161 | // ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments) 162 | // and asserts that the first time.Time does NOT happen within or on the duration specified relative to 163 | // the other time.Time. 164 | func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string { 165 | if fail := need(2, expected); fail != success { 166 | return fail 167 | } 168 | actualTime, firstOk := actual.(time.Time) 169 | tolerance, secondOk := expected[0].(time.Duration) 170 | threshold, thirdOk := expected[1].(time.Time) 171 | 172 | if !firstOk || !secondOk || !thirdOk { 173 | return shouldUseDurationAndTime 174 | } 175 | 176 | min := threshold.Add(-tolerance) 177 | max := threshold.Add(tolerance) 178 | return ShouldNotHappenOnOrBetween(actualTime, min, max) 179 | } 180 | 181 | // ShouldBeChronological receives a []time.Time slice and asserts that the are 182 | // in chronological order starting with the first time.Time as the earliest. 183 | func ShouldBeChronological(actual interface{}, expected ...interface{}) string { 184 | if fail := need(0, expected); fail != success { 185 | return fail 186 | } 187 | 188 | times, ok := actual.([]time.Time) 189 | if !ok { 190 | return shouldUseTimeSlice 191 | } 192 | 193 | var previous time.Time 194 | for i, current := range times { 195 | if i > 0 && current.Before(previous) { 196 | return fmt.Sprintf(shouldHaveBeenChronological, 197 | i, i-1, previous.String(), i, current.String()) 198 | } 199 | previous = current 200 | } 201 | return "" 202 | } 203 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/assertions/type.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // ShouldHaveSameTypeAs receives exactly two parameters and compares their underlying types for equality. 9 | func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string { 10 | if fail := need(1, expected); fail != success { 11 | return fail 12 | } 13 | 14 | first := reflect.TypeOf(actual) 15 | second := reflect.TypeOf(expected[0]) 16 | 17 | if first != second { 18 | return serializer.serialize(second, first, fmt.Sprintf(shouldHaveBeenA, actual, second, first)) 19 | } 20 | 21 | return success 22 | } 23 | 24 | // ShouldNotHaveSameTypeAs receives exactly two parameters and compares their underlying types for inequality. 25 | func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string { 26 | if fail := need(1, expected); fail != success { 27 | return fail 28 | } 29 | 30 | first := reflect.TypeOf(actual) 31 | second := reflect.TypeOf(expected[0]) 32 | 33 | if (actual == nil && expected[0] == nil) || first == second { 34 | return fmt.Sprintf(shouldNotHaveBeenA, actual, second) 35 | } 36 | return success 37 | } 38 | 39 | // ShouldImplement receives exactly two parameters and ensures 40 | // that the first implements the interface type of the second. 41 | func ShouldImplement(actual interface{}, expectedList ...interface{}) string { 42 | if fail := need(1, expectedList); fail != success { 43 | return fail 44 | } 45 | 46 | expected := expectedList[0] 47 | if fail := ShouldBeNil(expected); fail != success { 48 | return shouldCompareWithInterfacePointer 49 | } 50 | 51 | if fail := ShouldNotBeNil(actual); fail != success { 52 | return shouldNotBeNilActual 53 | } 54 | 55 | var actualType reflect.Type 56 | if reflect.TypeOf(actual).Kind() != reflect.Ptr { 57 | actualType = reflect.PtrTo(reflect.TypeOf(actual)) 58 | } else { 59 | actualType = reflect.TypeOf(actual) 60 | } 61 | 62 | expectedType := reflect.TypeOf(expected) 63 | if fail := ShouldNotBeNil(expectedType); fail != success { 64 | return shouldCompareWithInterfacePointer 65 | } 66 | 67 | expectedInterface := expectedType.Elem() 68 | 69 | if !actualType.Implements(expectedInterface) { 70 | return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actualType) 71 | } 72 | return success 73 | } 74 | 75 | // ShouldNotImplement receives exactly two parameters and ensures 76 | // that the first does NOT implement the interface type of the second. 77 | func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string { 78 | if fail := need(1, expectedList); fail != success { 79 | return fail 80 | } 81 | 82 | expected := expectedList[0] 83 | if fail := ShouldBeNil(expected); fail != success { 84 | return shouldCompareWithInterfacePointer 85 | } 86 | 87 | if fail := ShouldNotBeNil(actual); fail != success { 88 | return shouldNotBeNilActual 89 | } 90 | 91 | var actualType reflect.Type 92 | if reflect.TypeOf(actual).Kind() != reflect.Ptr { 93 | actualType = reflect.PtrTo(reflect.TypeOf(actual)) 94 | } else { 95 | actualType = reflect.TypeOf(actual) 96 | } 97 | 98 | expectedType := reflect.TypeOf(expected) 99 | if fail := ShouldNotBeNil(expectedType); fail != success { 100 | return shouldCompareWithInterfacePointer 101 | } 102 | 103 | expectedInterface := expectedType.Elem() 104 | 105 | if actualType.Implements(expectedInterface) { 106 | return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface) 107 | } 108 | return success 109 | } 110 | 111 | // ShouldBeError asserts that the first argument implements the error interface. 112 | // It also compares the first argument against the second argument if provided 113 | // (which must be an error message string or another error value). 114 | func ShouldBeError(actual interface{}, expected ...interface{}) string { 115 | if fail := atMost(1, expected); fail != success { 116 | return fail 117 | } 118 | 119 | if !isError(actual) { 120 | return fmt.Sprintf(shouldBeError, reflect.TypeOf(actual)) 121 | } 122 | 123 | if len(expected) == 0 { 124 | return success 125 | } 126 | 127 | if expected := expected[0]; !isString(expected) && !isError(expected) { 128 | return fmt.Sprintf(shouldBeErrorInvalidComparisonValue, reflect.TypeOf(expected)) 129 | } 130 | return ShouldEqual(fmt.Sprint(actual), fmt.Sprint(expected[0])) 131 | } 132 | 133 | func isString(value interface{}) bool { _, ok := value.(string); return ok } 134 | func isError(value interface{}) bool { _, ok := value.(error); return ok } 135 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 SmartyStreets, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | NOTE: Various optional and subordinate components carry their own licensing 22 | requirements and restrictions. Use of those components is subject to the terms 23 | and conditions outlined the respective license of each component. 24 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/assertions.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import "github.com/smartystreets/assertions" 4 | 5 | var ( 6 | ShouldEqual = assertions.ShouldEqual 7 | ShouldNotEqual = assertions.ShouldNotEqual 8 | ShouldAlmostEqual = assertions.ShouldAlmostEqual 9 | ShouldNotAlmostEqual = assertions.ShouldNotAlmostEqual 10 | ShouldResemble = assertions.ShouldResemble 11 | ShouldNotResemble = assertions.ShouldNotResemble 12 | ShouldPointTo = assertions.ShouldPointTo 13 | ShouldNotPointTo = assertions.ShouldNotPointTo 14 | ShouldBeNil = assertions.ShouldBeNil 15 | ShouldNotBeNil = assertions.ShouldNotBeNil 16 | ShouldBeTrue = assertions.ShouldBeTrue 17 | ShouldBeFalse = assertions.ShouldBeFalse 18 | ShouldBeZeroValue = assertions.ShouldBeZeroValue 19 | 20 | ShouldBeGreaterThan = assertions.ShouldBeGreaterThan 21 | ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo 22 | ShouldBeLessThan = assertions.ShouldBeLessThan 23 | ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo 24 | ShouldBeBetween = assertions.ShouldBeBetween 25 | ShouldNotBeBetween = assertions.ShouldNotBeBetween 26 | ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual 27 | ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual 28 | 29 | ShouldContain = assertions.ShouldContain 30 | ShouldNotContain = assertions.ShouldNotContain 31 | ShouldContainKey = assertions.ShouldContainKey 32 | ShouldNotContainKey = assertions.ShouldNotContainKey 33 | ShouldBeIn = assertions.ShouldBeIn 34 | ShouldNotBeIn = assertions.ShouldNotBeIn 35 | ShouldBeEmpty = assertions.ShouldBeEmpty 36 | ShouldNotBeEmpty = assertions.ShouldNotBeEmpty 37 | ShouldHaveLength = assertions.ShouldHaveLength 38 | 39 | ShouldStartWith = assertions.ShouldStartWith 40 | ShouldNotStartWith = assertions.ShouldNotStartWith 41 | ShouldEndWith = assertions.ShouldEndWith 42 | ShouldNotEndWith = assertions.ShouldNotEndWith 43 | ShouldBeBlank = assertions.ShouldBeBlank 44 | ShouldNotBeBlank = assertions.ShouldNotBeBlank 45 | ShouldContainSubstring = assertions.ShouldContainSubstring 46 | ShouldNotContainSubstring = assertions.ShouldNotContainSubstring 47 | 48 | ShouldPanic = assertions.ShouldPanic 49 | ShouldNotPanic = assertions.ShouldNotPanic 50 | ShouldPanicWith = assertions.ShouldPanicWith 51 | ShouldNotPanicWith = assertions.ShouldNotPanicWith 52 | 53 | ShouldHaveSameTypeAs = assertions.ShouldHaveSameTypeAs 54 | ShouldNotHaveSameTypeAs = assertions.ShouldNotHaveSameTypeAs 55 | ShouldImplement = assertions.ShouldImplement 56 | ShouldNotImplement = assertions.ShouldNotImplement 57 | 58 | ShouldHappenBefore = assertions.ShouldHappenBefore 59 | ShouldHappenOnOrBefore = assertions.ShouldHappenOnOrBefore 60 | ShouldHappenAfter = assertions.ShouldHappenAfter 61 | ShouldHappenOnOrAfter = assertions.ShouldHappenOnOrAfter 62 | ShouldHappenBetween = assertions.ShouldHappenBetween 63 | ShouldHappenOnOrBetween = assertions.ShouldHappenOnOrBetween 64 | ShouldNotHappenOnOrBetween = assertions.ShouldNotHappenOnOrBetween 65 | ShouldHappenWithin = assertions.ShouldHappenWithin 66 | ShouldNotHappenWithin = assertions.ShouldNotHappenWithin 67 | ShouldBeChronological = assertions.ShouldBeChronological 68 | 69 | ShouldBeError = assertions.ShouldBeError 70 | ) 71 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/convey.goconvey: -------------------------------------------------------------------------------- 1 | #ignore 2 | -timeout=1s 3 | #-covermode=count 4 | #-coverpkg=github.com/smartystreets/goconvey/convey,github.com/smartystreets/goconvey/convey/gotest,github.com/smartystreets/goconvey/convey/reporting -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/discovery.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | type actionSpecifier uint8 4 | 5 | const ( 6 | noSpecifier actionSpecifier = iota 7 | skipConvey 8 | focusConvey 9 | ) 10 | 11 | type suite struct { 12 | Situation string 13 | Test t 14 | Focus bool 15 | Func func(C) // nil means skipped 16 | FailMode FailureMode 17 | } 18 | 19 | func newSuite(situation string, failureMode FailureMode, f func(C), test t, specifier actionSpecifier) *suite { 20 | ret := &suite{ 21 | Situation: situation, 22 | Test: test, 23 | Func: f, 24 | FailMode: failureMode, 25 | } 26 | switch specifier { 27 | case skipConvey: 28 | ret.Func = nil 29 | case focusConvey: 30 | ret.Focus = true 31 | } 32 | return ret 33 | } 34 | 35 | func discover(items []interface{}) *suite { 36 | name, items := parseName(items) 37 | test, items := parseGoTest(items) 38 | failure, items := parseFailureMode(items) 39 | action, items := parseAction(items) 40 | specifier, items := parseSpecifier(items) 41 | 42 | if len(items) != 0 { 43 | conveyPanic(parseError) 44 | } 45 | 46 | return newSuite(name, failure, action, test, specifier) 47 | } 48 | func item(items []interface{}) interface{} { 49 | if len(items) == 0 { 50 | conveyPanic(parseError) 51 | } 52 | return items[0] 53 | } 54 | func parseName(items []interface{}) (string, []interface{}) { 55 | if name, parsed := item(items).(string); parsed { 56 | return name, items[1:] 57 | } 58 | conveyPanic(parseError) 59 | panic("never get here") 60 | } 61 | func parseGoTest(items []interface{}) (t, []interface{}) { 62 | if test, parsed := item(items).(t); parsed { 63 | return test, items[1:] 64 | } 65 | return nil, items 66 | } 67 | func parseFailureMode(items []interface{}) (FailureMode, []interface{}) { 68 | if mode, parsed := item(items).(FailureMode); parsed { 69 | return mode, items[1:] 70 | } 71 | return FailureInherits, items 72 | } 73 | func parseAction(items []interface{}) (func(C), []interface{}) { 74 | switch x := item(items).(type) { 75 | case nil: 76 | return nil, items[1:] 77 | case func(C): 78 | return x, items[1:] 79 | case func(): 80 | return func(C) { x() }, items[1:] 81 | } 82 | conveyPanic(parseError) 83 | panic("never get here") 84 | } 85 | func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) { 86 | if len(items) == 0 { 87 | return noSpecifier, items 88 | } 89 | if spec, ok := items[0].(actionSpecifier); ok { 90 | return spec, items[1:] 91 | } 92 | conveyPanic(parseError) 93 | panic("never get here") 94 | } 95 | 96 | // This interface allows us to pass the *testing.T struct 97 | // throughout the internals of this package without ever 98 | // having to import the "testing" package. 99 | type t interface { 100 | Fail() 101 | } 102 | 103 | const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode, and then an action (func())." 104 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go: -------------------------------------------------------------------------------- 1 | // Package gotest contains internal functionality. Although this package 2 | // contains one or more exported names it is not intended for public 3 | // consumption. See the examples package for how to use this project. 4 | package gotest 5 | 6 | import ( 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | func ResolveExternalCaller() (file string, line int, name string) { 12 | var caller_id uintptr 13 | callers := runtime.Callers(0, callStack) 14 | 15 | for x := 0; x < callers; x++ { 16 | caller_id, file, line, _ = runtime.Caller(x) 17 | if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") { 18 | name = runtime.FuncForPC(caller_id).Name() 19 | return 20 | } 21 | } 22 | file, line, name = "", -1, "" 23 | return // panic? 24 | } 25 | 26 | const maxStackDepth = 100 // This had better be enough... 27 | 28 | var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth) 29 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/init.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | 7 | "github.com/jtolds/gls" 8 | "github.com/smartystreets/assertions" 9 | "github.com/smartystreets/goconvey/convey/reporting" 10 | ) 11 | 12 | func init() { 13 | assertions.GoConveyMode(true) 14 | 15 | declareFlags() 16 | 17 | ctxMgr = gls.NewContextManager() 18 | } 19 | 20 | func declareFlags() { 21 | flag.BoolVar(&json, "convey-json", false, "When true, emits results in JSON blocks. Default: 'false'") 22 | flag.BoolVar(&silent, "convey-silent", false, "When true, all output from GoConvey is suppressed.") 23 | flag.BoolVar(&story, "convey-story", false, "When true, emits story output, otherwise emits dot output. When not provided, this flag mirros the value of the '-test.v' flag") 24 | 25 | if noStoryFlagProvided() { 26 | story = verboseEnabled 27 | } 28 | 29 | // FYI: flag.Parse() is called from the testing package. 30 | } 31 | 32 | func noStoryFlagProvided() bool { 33 | return !story && !storyDisabled 34 | } 35 | 36 | func buildReporter() reporting.Reporter { 37 | selectReporter := os.Getenv("GOCONVEY_REPORTER") 38 | 39 | switch { 40 | case testReporter != nil: 41 | return testReporter 42 | case json || selectReporter == "json": 43 | return reporting.BuildJsonReporter() 44 | case silent || selectReporter == "silent": 45 | return reporting.BuildSilentReporter() 46 | case selectReporter == "dot": 47 | // Story is turned on when verbose is set, so we need to check for dot reporter first. 48 | return reporting.BuildDotReporter() 49 | case story || selectReporter == "story": 50 | return reporting.BuildStoryReporter() 51 | default: 52 | return reporting.BuildDotReporter() 53 | } 54 | } 55 | 56 | var ( 57 | ctxMgr *gls.ContextManager 58 | 59 | // only set by internal tests 60 | testReporter reporting.Reporter 61 | ) 62 | 63 | var ( 64 | json bool 65 | silent bool 66 | story bool 67 | 68 | verboseEnabled = flagFound("-test.v=true") 69 | storyDisabled = flagFound("-story=false") 70 | ) 71 | 72 | // flagFound parses the command line args manually for flags defined in other 73 | // packages. Like the '-v' flag from the "testing" package, for instance. 74 | func flagFound(flagValue string) bool { 75 | for _, arg := range os.Args { 76 | if arg == flagValue { 77 | return true 78 | } 79 | } 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/nilReporter.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import ( 4 | "github.com/smartystreets/goconvey/convey/reporting" 5 | ) 6 | 7 | type nilReporter struct{} 8 | 9 | func (self *nilReporter) BeginStory(story *reporting.StoryReport) {} 10 | func (self *nilReporter) Enter(scope *reporting.ScopeReport) {} 11 | func (self *nilReporter) Report(report *reporting.AssertionResult) {} 12 | func (self *nilReporter) Exit() {} 13 | func (self *nilReporter) EndStory() {} 14 | func (self *nilReporter) Write(p []byte) (int, error) { return len(p), nil } 15 | func newNilReporter() *nilReporter { return &nilReporter{} } 16 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/console.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | type console struct{} 9 | 10 | func (self *console) Write(p []byte) (n int, err error) { 11 | return fmt.Print(string(p)) 12 | } 13 | 14 | func NewConsole() io.Writer { 15 | return new(console) 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go: -------------------------------------------------------------------------------- 1 | // Package reporting contains internal functionality related 2 | // to console reporting and output. Although this package has 3 | // exported names is not intended for public consumption. See the 4 | // examples package for how to use this project. 5 | package reporting 6 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "fmt" 4 | 5 | type dot struct{ out *Printer } 6 | 7 | func (self *dot) BeginStory(story *StoryReport) {} 8 | 9 | func (self *dot) Enter(scope *ScopeReport) {} 10 | 11 | func (self *dot) Report(report *AssertionResult) { 12 | if report.Error != nil { 13 | fmt.Print(redColor) 14 | self.out.Insert(dotError) 15 | } else if report.Failure != "" { 16 | fmt.Print(yellowColor) 17 | self.out.Insert(dotFailure) 18 | } else if report.Skipped { 19 | fmt.Print(yellowColor) 20 | self.out.Insert(dotSkip) 21 | } else { 22 | fmt.Print(greenColor) 23 | self.out.Insert(dotSuccess) 24 | } 25 | fmt.Print(resetColor) 26 | } 27 | 28 | func (self *dot) Exit() {} 29 | 30 | func (self *dot) EndStory() {} 31 | 32 | func (self *dot) Write(content []byte) (written int, err error) { 33 | return len(content), nil // no-op 34 | } 35 | 36 | func NewDotReporter(out *Printer) *dot { 37 | self := new(dot) 38 | self.out = out 39 | return self 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | type gotestReporter struct{ test T } 4 | 5 | func (self *gotestReporter) BeginStory(story *StoryReport) { 6 | self.test = story.Test 7 | } 8 | 9 | func (self *gotestReporter) Enter(scope *ScopeReport) {} 10 | 11 | func (self *gotestReporter) Report(r *AssertionResult) { 12 | if !passed(r) { 13 | self.test.Fail() 14 | } 15 | } 16 | 17 | func (self *gotestReporter) Exit() {} 18 | 19 | func (self *gotestReporter) EndStory() { 20 | self.test = nil 21 | } 22 | 23 | func (self *gotestReporter) Write(content []byte) (written int, err error) { 24 | return len(content), nil // no-op 25 | } 26 | 27 | func NewGoTestReporter() *gotestReporter { 28 | return new(gotestReporter) 29 | } 30 | 31 | func passed(r *AssertionResult) bool { 32 | return r.Error == nil && r.Failure == "" 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/init.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | func init() { 10 | if !isColorableTerminal() { 11 | monochrome() 12 | } 13 | 14 | if runtime.GOOS == "windows" { 15 | success, failure, error_ = dotSuccess, dotFailure, dotError 16 | } 17 | } 18 | 19 | func BuildJsonReporter() Reporter { 20 | out := NewPrinter(NewConsole()) 21 | return NewReporters( 22 | NewGoTestReporter(), 23 | NewJsonReporter(out)) 24 | } 25 | func BuildDotReporter() Reporter { 26 | out := NewPrinter(NewConsole()) 27 | return NewReporters( 28 | NewGoTestReporter(), 29 | NewDotReporter(out), 30 | NewProblemReporter(out), 31 | consoleStatistics) 32 | } 33 | func BuildStoryReporter() Reporter { 34 | out := NewPrinter(NewConsole()) 35 | return NewReporters( 36 | NewGoTestReporter(), 37 | NewStoryReporter(out), 38 | NewProblemReporter(out), 39 | consoleStatistics) 40 | } 41 | func BuildSilentReporter() Reporter { 42 | out := NewPrinter(NewConsole()) 43 | return NewReporters( 44 | NewGoTestReporter(), 45 | NewSilentProblemReporter(out)) 46 | } 47 | 48 | var ( 49 | newline = "\n" 50 | success = "✔" 51 | failure = "✘" 52 | error_ = "🔥" 53 | skip = "⚠" 54 | dotSuccess = "." 55 | dotFailure = "x" 56 | dotError = "E" 57 | dotSkip = "S" 58 | errorTemplate = "* %s \nLine %d: - %v \n%s\n" 59 | failureTemplate = "* %s \nLine %d:\n%s\n" 60 | ) 61 | 62 | var ( 63 | greenColor = "\033[32m" 64 | yellowColor = "\033[33m" 65 | redColor = "\033[31m" 66 | resetColor = "\033[0m" 67 | ) 68 | 69 | var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole())) 70 | 71 | func SuppressConsoleStatistics() { consoleStatistics.Suppress() } 72 | func PrintConsoleStatistics() { consoleStatistics.PrintSummary() } 73 | 74 | // QuiteMode disables all console output symbols. This is only meant to be used 75 | // for tests that are internal to goconvey where the output is distracting or 76 | // otherwise not needed in the test output. 77 | func QuietMode() { 78 | success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", "" 79 | } 80 | 81 | func monochrome() { 82 | greenColor, yellowColor, redColor, resetColor = "", "", "", "" 83 | } 84 | 85 | func isColorableTerminal() bool { 86 | return strings.Contains(os.Getenv("TERM"), "color") 87 | } 88 | 89 | // This interface allows us to pass the *testing.T struct 90 | // throughout the internals of this tool without ever 91 | // having to import the "testing" package. 92 | type T interface { 93 | Fail() 94 | } 95 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/json.go: -------------------------------------------------------------------------------- 1 | // TODO: under unit test 2 | 3 | package reporting 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | type JsonReporter struct { 13 | out *Printer 14 | currentKey []string 15 | current *ScopeResult 16 | index map[string]*ScopeResult 17 | scopes []*ScopeResult 18 | } 19 | 20 | func (self *JsonReporter) depth() int { return len(self.currentKey) } 21 | 22 | func (self *JsonReporter) BeginStory(story *StoryReport) {} 23 | 24 | func (self *JsonReporter) Enter(scope *ScopeReport) { 25 | self.currentKey = append(self.currentKey, scope.Title) 26 | ID := strings.Join(self.currentKey, "|") 27 | if _, found := self.index[ID]; !found { 28 | next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line) 29 | self.scopes = append(self.scopes, next) 30 | self.index[ID] = next 31 | } 32 | self.current = self.index[ID] 33 | } 34 | 35 | func (self *JsonReporter) Report(report *AssertionResult) { 36 | self.current.Assertions = append(self.current.Assertions, report) 37 | } 38 | 39 | func (self *JsonReporter) Exit() { 40 | self.currentKey = self.currentKey[:len(self.currentKey)-1] 41 | } 42 | 43 | func (self *JsonReporter) EndStory() { 44 | self.report() 45 | self.reset() 46 | } 47 | func (self *JsonReporter) report() { 48 | scopes := []string{} 49 | for _, scope := range self.scopes { 50 | serialized, err := json.Marshal(scope) 51 | if err != nil { 52 | self.out.Println(jsonMarshalFailure) 53 | panic(err) 54 | } 55 | var buffer bytes.Buffer 56 | json.Indent(&buffer, serialized, "", " ") 57 | scopes = append(scopes, buffer.String()) 58 | } 59 | self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson)) 60 | } 61 | func (self *JsonReporter) reset() { 62 | self.scopes = []*ScopeResult{} 63 | self.index = map[string]*ScopeResult{} 64 | self.currentKey = nil 65 | } 66 | 67 | func (self *JsonReporter) Write(content []byte) (written int, err error) { 68 | self.current.Output += string(content) 69 | return len(content), nil 70 | } 71 | 72 | func NewJsonReporter(out *Printer) *JsonReporter { 73 | self := new(JsonReporter) 74 | self.out = out 75 | self.reset() 76 | return self 77 | } 78 | 79 | const OpenJson = ">->->OPEN-JSON->->->" // "⌦" 80 | const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫" 81 | const jsonMarshalFailure = ` 82 | 83 | GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON. 84 | Please file a bug report and reference the code that caused this failure if possible. 85 | 86 | Here's the panic: 87 | 88 | ` 89 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | type Printer struct { 10 | out io.Writer 11 | prefix string 12 | } 13 | 14 | func (self *Printer) Println(message string, values ...interface{}) { 15 | formatted := self.format(message, values...) + newline 16 | self.out.Write([]byte(formatted)) 17 | } 18 | 19 | func (self *Printer) Print(message string, values ...interface{}) { 20 | formatted := self.format(message, values...) 21 | self.out.Write([]byte(formatted)) 22 | } 23 | 24 | func (self *Printer) Insert(text string) { 25 | self.out.Write([]byte(text)) 26 | } 27 | 28 | func (self *Printer) format(message string, values ...interface{}) string { 29 | var formatted string 30 | if len(values) == 0 { 31 | formatted = self.prefix + message 32 | } else { 33 | formatted = self.prefix + fmt.Sprintf(message, values...) 34 | } 35 | indented := strings.Replace(formatted, newline, newline+self.prefix, -1) 36 | return strings.TrimRight(indented, space) 37 | } 38 | 39 | func (self *Printer) Indent() { 40 | self.prefix += pad 41 | } 42 | 43 | func (self *Printer) Dedent() { 44 | if len(self.prefix) >= padLength { 45 | self.prefix = self.prefix[:len(self.prefix)-padLength] 46 | } 47 | } 48 | 49 | func NewPrinter(out io.Writer) *Printer { 50 | self := new(Printer) 51 | self.out = out 52 | return self 53 | } 54 | 55 | const space = " " 56 | const pad = space + space 57 | const padLength = len(pad) 58 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "fmt" 4 | 5 | type problem struct { 6 | silent bool 7 | out *Printer 8 | errors []*AssertionResult 9 | failures []*AssertionResult 10 | } 11 | 12 | func (self *problem) BeginStory(story *StoryReport) {} 13 | 14 | func (self *problem) Enter(scope *ScopeReport) {} 15 | 16 | func (self *problem) Report(report *AssertionResult) { 17 | if report.Error != nil { 18 | self.errors = append(self.errors, report) 19 | } else if report.Failure != "" { 20 | self.failures = append(self.failures, report) 21 | } 22 | } 23 | 24 | func (self *problem) Exit() {} 25 | 26 | func (self *problem) EndStory() { 27 | self.show(self.showErrors, redColor) 28 | self.show(self.showFailures, yellowColor) 29 | self.prepareForNextStory() 30 | } 31 | func (self *problem) show(display func(), color string) { 32 | if !self.silent { 33 | fmt.Print(color) 34 | } 35 | display() 36 | if !self.silent { 37 | fmt.Print(resetColor) 38 | } 39 | self.out.Dedent() 40 | } 41 | func (self *problem) showErrors() { 42 | for i, e := range self.errors { 43 | if i == 0 { 44 | self.out.Println("\nErrors:\n") 45 | self.out.Indent() 46 | } 47 | self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace) 48 | } 49 | } 50 | func (self *problem) showFailures() { 51 | for i, f := range self.failures { 52 | if i == 0 { 53 | self.out.Println("\nFailures:\n") 54 | self.out.Indent() 55 | } 56 | self.out.Println(failureTemplate, f.File, f.Line, f.Failure) 57 | } 58 | } 59 | 60 | func (self *problem) Write(content []byte) (written int, err error) { 61 | return len(content), nil // no-op 62 | } 63 | 64 | func NewProblemReporter(out *Printer) *problem { 65 | self := new(problem) 66 | self.out = out 67 | self.prepareForNextStory() 68 | return self 69 | } 70 | 71 | func NewSilentProblemReporter(out *Printer) *problem { 72 | self := NewProblemReporter(out) 73 | self.silent = true 74 | return self 75 | } 76 | 77 | func (self *problem) prepareForNextStory() { 78 | self.errors = []*AssertionResult{} 79 | self.failures = []*AssertionResult{} 80 | } 81 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "io" 4 | 5 | type Reporter interface { 6 | BeginStory(story *StoryReport) 7 | Enter(scope *ScopeReport) 8 | Report(r *AssertionResult) 9 | Exit() 10 | EndStory() 11 | io.Writer 12 | } 13 | 14 | type reporters struct{ collection []Reporter } 15 | 16 | func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) } 17 | func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) } 18 | func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) } 19 | func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) } 20 | func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) } 21 | 22 | func (self *reporters) Write(contents []byte) (written int, err error) { 23 | self.foreach(func(r Reporter) { 24 | written, err = r.Write(contents) 25 | }) 26 | return written, err 27 | } 28 | 29 | func (self *reporters) foreach(action func(Reporter)) { 30 | for _, r := range self.collection { 31 | action(r) 32 | } 33 | } 34 | 35 | func NewReporters(collection ...Reporter) *reporters { 36 | self := new(reporters) 37 | self.collection = collection 38 | return self 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey: -------------------------------------------------------------------------------- 1 | #ignore 2 | -timeout=1s 3 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "runtime" 7 | "strings" 8 | 9 | "github.com/smartystreets/goconvey/convey/gotest" 10 | ) 11 | 12 | ////////////////// ScopeReport //////////////////// 13 | 14 | type ScopeReport struct { 15 | Title string 16 | File string 17 | Line int 18 | } 19 | 20 | func NewScopeReport(title string) *ScopeReport { 21 | file, line, _ := gotest.ResolveExternalCaller() 22 | self := new(ScopeReport) 23 | self.Title = title 24 | self.File = file 25 | self.Line = line 26 | return self 27 | } 28 | 29 | ////////////////// ScopeResult //////////////////// 30 | 31 | type ScopeResult struct { 32 | Title string 33 | File string 34 | Line int 35 | Depth int 36 | Assertions []*AssertionResult 37 | Output string 38 | } 39 | 40 | func newScopeResult(title string, depth int, file string, line int) *ScopeResult { 41 | self := new(ScopeResult) 42 | self.Title = title 43 | self.Depth = depth 44 | self.File = file 45 | self.Line = line 46 | self.Assertions = []*AssertionResult{} 47 | return self 48 | } 49 | 50 | /////////////////// StoryReport ///////////////////// 51 | 52 | type StoryReport struct { 53 | Test T 54 | Name string 55 | File string 56 | Line int 57 | } 58 | 59 | func NewStoryReport(test T) *StoryReport { 60 | file, line, name := gotest.ResolveExternalCaller() 61 | name = removePackagePath(name) 62 | self := new(StoryReport) 63 | self.Test = test 64 | self.Name = name 65 | self.File = file 66 | self.Line = line 67 | return self 68 | } 69 | 70 | // name comes in looking like "github.com/smartystreets/goconvey/examples.TestName". 71 | // We only want the stuff after the last '.', which is the name of the test function. 72 | func removePackagePath(name string) string { 73 | parts := strings.Split(name, ".") 74 | return parts[len(parts)-1] 75 | } 76 | 77 | /////////////////// FailureView //////////////////////// 78 | 79 | // This struct is also declared in github.com/smartystreets/assertions. 80 | // The json struct tags should be equal in both declarations. 81 | type FailureView struct { 82 | Message string `json:"Message"` 83 | Expected string `json:"Expected"` 84 | Actual string `json:"Actual"` 85 | } 86 | 87 | ////////////////////AssertionResult ////////////////////// 88 | 89 | type AssertionResult struct { 90 | File string 91 | Line int 92 | Expected string 93 | Actual string 94 | Failure string 95 | Error interface{} 96 | StackTrace string 97 | Skipped bool 98 | } 99 | 100 | func NewFailureReport(failure string) *AssertionResult { 101 | report := new(AssertionResult) 102 | report.File, report.Line = caller() 103 | report.StackTrace = stackTrace() 104 | parseFailure(failure, report) 105 | return report 106 | } 107 | func parseFailure(failure string, report *AssertionResult) { 108 | view := new(FailureView) 109 | err := json.Unmarshal([]byte(failure), view) 110 | if err == nil { 111 | report.Failure = view.Message 112 | report.Expected = view.Expected 113 | report.Actual = view.Actual 114 | } else { 115 | report.Failure = failure 116 | } 117 | } 118 | func NewErrorReport(err interface{}) *AssertionResult { 119 | report := new(AssertionResult) 120 | report.File, report.Line = caller() 121 | report.StackTrace = fullStackTrace() 122 | report.Error = fmt.Sprintf("%v", err) 123 | return report 124 | } 125 | func NewSuccessReport() *AssertionResult { 126 | return new(AssertionResult) 127 | } 128 | func NewSkipReport() *AssertionResult { 129 | report := new(AssertionResult) 130 | report.File, report.Line = caller() 131 | report.StackTrace = fullStackTrace() 132 | report.Skipped = true 133 | return report 134 | } 135 | 136 | func caller() (file string, line int) { 137 | file, line, _ = gotest.ResolveExternalCaller() 138 | return 139 | } 140 | 141 | func stackTrace() string { 142 | buffer := make([]byte, 1024*64) 143 | n := runtime.Stack(buffer, false) 144 | return removeInternalEntries(string(buffer[:n])) 145 | } 146 | func fullStackTrace() string { 147 | buffer := make([]byte, 1024*64) 148 | n := runtime.Stack(buffer, true) 149 | return removeInternalEntries(string(buffer[:n])) 150 | } 151 | func removeInternalEntries(stack string) string { 152 | lines := strings.Split(stack, newline) 153 | filtered := []string{} 154 | for _, line := range lines { 155 | if !isExternal(line) { 156 | filtered = append(filtered, line) 157 | } 158 | } 159 | return strings.Join(filtered, newline) 160 | } 161 | func isExternal(line string) bool { 162 | for _, p := range internalPackages { 163 | if strings.Contains(line, p) { 164 | return true 165 | } 166 | } 167 | return false 168 | } 169 | 170 | // NOTE: any new packages that host goconvey packages will need to be added here! 171 | // An alternative is to scan the goconvey directory and then exclude stuff like 172 | // the examples package but that's nasty too. 173 | var internalPackages = []string{ 174 | "goconvey/assertions", 175 | "goconvey/convey", 176 | "goconvey/execution", 177 | "goconvey/gotest", 178 | "goconvey/reporting", 179 | } 180 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func (self *statistics) BeginStory(story *StoryReport) {} 9 | 10 | func (self *statistics) Enter(scope *ScopeReport) {} 11 | 12 | func (self *statistics) Report(report *AssertionResult) { 13 | self.Lock() 14 | defer self.Unlock() 15 | 16 | if !self.failing && report.Failure != "" { 17 | self.failing = true 18 | } 19 | if !self.erroring && report.Error != nil { 20 | self.erroring = true 21 | } 22 | if report.Skipped { 23 | self.skipped += 1 24 | } else { 25 | self.total++ 26 | } 27 | } 28 | 29 | func (self *statistics) Exit() {} 30 | 31 | func (self *statistics) EndStory() { 32 | self.Lock() 33 | defer self.Unlock() 34 | 35 | if !self.suppressed { 36 | self.printSummaryLocked() 37 | } 38 | } 39 | 40 | func (self *statistics) Suppress() { 41 | self.Lock() 42 | defer self.Unlock() 43 | self.suppressed = true 44 | } 45 | 46 | func (self *statistics) PrintSummary() { 47 | self.Lock() 48 | defer self.Unlock() 49 | self.printSummaryLocked() 50 | } 51 | 52 | func (self *statistics) printSummaryLocked() { 53 | self.reportAssertionsLocked() 54 | self.reportSkippedSectionsLocked() 55 | self.completeReportLocked() 56 | } 57 | func (self *statistics) reportAssertionsLocked() { 58 | self.decideColorLocked() 59 | self.out.Print("\n%d total %s", self.total, plural("assertion", self.total)) 60 | } 61 | func (self *statistics) decideColorLocked() { 62 | if self.failing && !self.erroring { 63 | fmt.Print(yellowColor) 64 | } else if self.erroring { 65 | fmt.Print(redColor) 66 | } else { 67 | fmt.Print(greenColor) 68 | } 69 | } 70 | func (self *statistics) reportSkippedSectionsLocked() { 71 | if self.skipped > 0 { 72 | fmt.Print(yellowColor) 73 | self.out.Print(" (one or more sections skipped)") 74 | } 75 | } 76 | func (self *statistics) completeReportLocked() { 77 | fmt.Print(resetColor) 78 | self.out.Print("\n") 79 | self.out.Print("\n") 80 | } 81 | 82 | func (self *statistics) Write(content []byte) (written int, err error) { 83 | return len(content), nil // no-op 84 | } 85 | 86 | func NewStatisticsReporter(out *Printer) *statistics { 87 | self := statistics{} 88 | self.out = out 89 | return &self 90 | } 91 | 92 | type statistics struct { 93 | sync.Mutex 94 | 95 | out *Printer 96 | total int 97 | failing bool 98 | erroring bool 99 | skipped int 100 | suppressed bool 101 | } 102 | 103 | func plural(word string, count int) string { 104 | if count == 1 { 105 | return word 106 | } 107 | return word + "s" 108 | } 109 | -------------------------------------------------------------------------------- /vendor/github.com/smartystreets/goconvey/convey/reporting/story.go: -------------------------------------------------------------------------------- 1 | // TODO: in order for this reporter to be completely honest 2 | // we need to retrofit to be more like the json reporter such that: 3 | // 1. it maintains ScopeResult collections, which count assertions 4 | // 2. it reports only after EndStory(), so that all tick marks 5 | // are placed near the appropriate title. 6 | // 3. Under unit test 7 | 8 | package reporting 9 | 10 | import ( 11 | "fmt" 12 | "strings" 13 | ) 14 | 15 | type story struct { 16 | out *Printer 17 | titlesById map[string]string 18 | currentKey []string 19 | } 20 | 21 | func (self *story) BeginStory(story *StoryReport) {} 22 | 23 | func (self *story) Enter(scope *ScopeReport) { 24 | self.out.Indent() 25 | 26 | self.currentKey = append(self.currentKey, scope.Title) 27 | ID := strings.Join(self.currentKey, "|") 28 | 29 | if _, found := self.titlesById[ID]; !found { 30 | self.out.Println("") 31 | self.out.Print(scope.Title) 32 | self.out.Insert(" ") 33 | self.titlesById[ID] = scope.Title 34 | } 35 | } 36 | 37 | func (self *story) Report(report *AssertionResult) { 38 | if report.Error != nil { 39 | fmt.Print(redColor) 40 | self.out.Insert(error_) 41 | } else if report.Failure != "" { 42 | fmt.Print(yellowColor) 43 | self.out.Insert(failure) 44 | } else if report.Skipped { 45 | fmt.Print(yellowColor) 46 | self.out.Insert(skip) 47 | } else { 48 | fmt.Print(greenColor) 49 | self.out.Insert(success) 50 | } 51 | fmt.Print(resetColor) 52 | } 53 | 54 | func (self *story) Exit() { 55 | self.out.Dedent() 56 | self.currentKey = self.currentKey[:len(self.currentKey)-1] 57 | } 58 | 59 | func (self *story) EndStory() { 60 | self.titlesById = make(map[string]string) 61 | self.out.Println("\n") 62 | } 63 | 64 | func (self *story) Write(content []byte) (written int, err error) { 65 | return len(content), nil // no-op 66 | } 67 | 68 | func NewStoryReporter(out *Printer) *story { 69 | self := new(story) 70 | self.out = out 71 | self.titlesById = make(map[string]string) 72 | return self 73 | } 74 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "rNGJereenlgchMueLU9iBCyQDo8=", 7 | "path": "github.com/gorilla/websocket", 8 | "revision": "71fa72d4842364bc5f74185f4161e0099ea3624a", 9 | "revisionTime": "2017-10-11T16:07:11Z" 10 | }, 11 | { 12 | "checksumSHA1": "Js/yx9fZ3+wH1wZpHNIxSTMIaCg=", 13 | "path": "github.com/jtolds/gls", 14 | "revision": "77f18212c9c7edc9bd6a33d383a7b545ce62f064", 15 | "revisionTime": "2017-05-03T22:40:06Z" 16 | }, 17 | { 18 | "checksumSHA1": "ozRFnFdTG0IjkTE0RkZn71XO4gw=", 19 | "path": "github.com/smartystreets/assertions", 20 | "revision": "0b37b35ec7434b77e77a4bb29b79677cced992ea", 21 | "revisionTime": "2017-09-25T17:21:51Z" 22 | }, 23 | { 24 | "checksumSHA1": "Vzb+dEH/LTYbvr8RXHmt6xJHz04=", 25 | "path": "github.com/smartystreets/assertions/internal/go-render/render", 26 | "revision": "0b37b35ec7434b77e77a4bb29b79677cced992ea", 27 | "revisionTime": "2017-09-25T17:21:51Z" 28 | }, 29 | { 30 | "checksumSHA1": "r6FauVdOTFnwYQgrKGFuWUbIAJE=", 31 | "path": "github.com/smartystreets/assertions/internal/oglematchers", 32 | "revision": "0b37b35ec7434b77e77a4bb29b79677cced992ea", 33 | "revisionTime": "2017-09-25T17:21:51Z" 34 | }, 35 | { 36 | "checksumSHA1": "f4m09DHEetaanti/GqUJzyCBTaI=", 37 | "path": "github.com/smartystreets/goconvey/convey", 38 | "revision": "e5b2b7c9111590d019a696c7800593f666e1a7f4", 39 | "revisionTime": "2017-08-25T22:14:26Z" 40 | }, 41 | { 42 | "checksumSHA1": "9LakndErFi5uCXtY1KWl0iRnT4c=", 43 | "path": "github.com/smartystreets/goconvey/convey/gotest", 44 | "revision": "e5b2b7c9111590d019a696c7800593f666e1a7f4", 45 | "revisionTime": "2017-08-25T22:14:26Z" 46 | }, 47 | { 48 | "checksumSHA1": "FWDhk37bhAwZ2363D/L2xePwR64=", 49 | "path": "github.com/smartystreets/goconvey/convey/reporting", 50 | "revision": "e5b2b7c9111590d019a696c7800593f666e1a7f4", 51 | "revisionTime": "2017-08-25T22:14:26Z" 52 | } 53 | ], 54 | "rootPath": "github.com/xwjdsh/2048-ai" 55 | } 56 | --------------------------------------------------------------------------------