├── 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 |
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 |
29 |
34 |
35 |
36 |
43 |
44 |
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 | [](https://travis-ci.org/xwjdsh/2048-ai)
4 | [](https://goreportcard.com/report/github.com/xwjdsh/2048-ai)
5 | [](https://microbadger.com/images/wendellsun/2048-ai "Get your own image badge on microbadger.com")
6 | [](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 | 
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 | [](https://travis-ci.org/gorilla/websocket)
7 | [](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 |
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 | [](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 |
--------------------------------------------------------------------------------